diff options
| author | Takashi Iwai <tiwai@suse.de> | 2026-08-18 08:35:39 +0200 |
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2026-08-18 08:35:39 +0200 |
| commit | b2b82ce1478eddd614a727e000180de95c53520d (patch) | |
| tree | 7ee06997947dc08dc26bdaeb4b0901e5e7a6df50 | |
| parent | ff722d025853a33a15b080459e4c52be28d44b6e (diff) | |
| parent | 0c7aeb0f5eceb95b5887bd8e83fef865e5a49a13 (diff) | |
Merge tag 'asoc-v7.3' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v7.3
A very busy release, a combination of a lot of new device work and more
people than usual doing various subsystem wide improvements in the code.
The main subsystem level change is the work Morimoto-san has been doing
to get automatic DAI format selection more widely deployed, and even
that is largely changes in the drivers to enable use of the feature
rather than framework work.
- Conversions of a lot of drivers to use the automatic DAI format
selection code from Morimoto-san.
- Many fixes and code improvements from bui duc phuc and Morimoto-san.
- Fixes for ordering problems in register default tables from Peter
Ujfalusi.
- Changes to use auto-cleanup for firmware to improve robustness from
Takashi Iwai.
- Support for firmware on more Awinc devices.
- Many changes in the Qualcomm driver stack, mostly around new platform
support.
- Support for AMD ACP7.B/F, Cirrus Logic CS35L62, Loongson 2K0300,
Meson GX formatter and interface, Qualcomm LPI MI2S, SM8475 and
WSA855 and Realtek RT1321 VA1/2 and RT766/7.
501 files changed, 15232 insertions, 6927 deletions
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml index 4f13e8ab50b2..177db900d4a0 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml @@ -30,7 +30,7 @@ properties: minItems: 1 maxItems: 3 description: - Base PLL clocks of audio susbsytem, used to configure base clock + Base PLL clocks of audio subsystem, used to configure base clock frequencies for different audio use-cases. patternProperties: diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml index 413b47778181..6fdf605ad59c 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml @@ -31,7 +31,7 @@ properties: minItems: 1 maxItems: 3 description: - Base PLL clocks of audio susbsytem, used to configure base clock + Base PLL clocks of audio subsystem, used to configure base clock frequencies for different audio use-cases. patternProperties: diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml new file mode 100644 index 000000000000..2a142b32acf5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml @@ -0,0 +1,240 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs35l36.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS35L36 Speaker Amplifier + +maintainers: + - David Rhodes <drhodes@opensource.cirrus.com> + - patches@opensource.cirrus.com + +description: + CS35L36 is a boosted mono Class D amplifier + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - cirrus,cs35l36 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + VA-supply: + description: Voltage regulator of analog internal section + + VP-supply: + description: Voltage regulator of boost converter + + reset-gpios: + maxItems: 1 + + cirrus,boost-ctl-millivolt: + description: Boost converter output voltage (step 50) + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2550 + maximum: 12000 + + cirrus,boost-peak-milliamp: + description: Boost-converter peak current limit (step 50) + $ref: /schemas/types.yaml#/definitions/uint32 + default: 4500 + minimum: 1600 + maximum: 4500 + + cirrus,boost-ind-nanohenry: + description: Initial inductor estimation reference value (1000=1μH, 1200=1.2μH) + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1000 + + cirrus,multi-amp-mode: + description: Hi-Z ASP port when more than one amplifier in system + type: boolean + + cirrus,boost-ctl-select: + description: Boost converter control source selection + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1 + enum: + - 0 # Control Port + - 1 # Class + - 2 # Sync + + cirrus,amp-pcm-inv: + description: Invert incoming PCM data when true + type: boolean + + cirrus,imon-pol-inv: + description: Invert polarity of outbound IMON feedback when true + type: boolean + + cirrus,vmon-pol-inv: + description: Invert polarity of outbound VMON feedback when true + type: boolean + + cirrus,dcm-mode-enable: + description: Enable boost converter automatic Discontinuous Conduction Mode + type: boolean + + cirrus,weak-fet-disable: + description: Reduce output driver strength in Weak-FET Drive Mode when true + type: boolean + + cirrus,classh-wk-fet-delay-ms: + description: Weak-FET entry delay + default: 100 + enum: [0, 5, 10, 50, 100, 200, 500, 1000] + + cirrus,classh-weak-fet-thld-millivolt: + description: Weak-FET drive threshold + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700] + + cirrus,temp-warn-threshold: + description: Overtemperature warning threshold + $ref: /schemas/types.yaml#/definitions/uint32 + default: 2 + enum: + - 0 # 105°C + - 1 # 115°C + - 2 # 125°C + - 3 # 135°C + + cirrus,irq-drive-select: + description: Interrupt output driver type + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1 + enum: + - 0 # open-drain + - 1 # push-pull + + cirrus,irq-gpio-select: + description: Programmable IRQ pin selection + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # PDM_DATA/SWIRE_SD/INT + - 1 # GPIO + + cirrus,vpbr-config: + $ref: "#/$defs/vpbr-config" + +$defs: + vpbr-config: + description: Brownout prevention configuration sub-node + type: object + additionalProperties: false + + properties: + cirrus,vpbr-en: + description: VBST brownout prevention enable + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + enum: + - 0 # disabled + - 1 # enabled + + cirrus,vpbr-thld: + description: Initial VPBR threshold voltage + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2 + maximum: 31 + + cirrus,vpbr-atk-rate: + description: Attenuation attack step rate + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + cirrus,vpbr-atk-vol: + description: VP brownout prevention step size + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + cirrus,vpbr-max-attn: + description: Maximum attenuation during VP brownout prevention (dB) + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + + cirrus,vpbr-wait: + description: Delay between brownout clearance and attenuation release (ms) + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1 + enum: + - 0 # 10 + - 1 # 100 + - 2 # 250 + - 3 # 500 + + cirrus,vpbr-rel-rate: + description: Attenuation release step rate + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + cirrus,vpbr-mute-en: + description: Mute audio if maximum attenuation reached + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 1 + +required: + - compatible + - reg + - interrupts + - VA-supply + - cirrus,boost-ctl-millivolt + - cirrus,boost-peak-milliamp + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@40 { + compatible = "cirrus,cs35l36"; + reg = <0x40>; + VA-supply = <&dummy_vreg>; + VP-supply = <&dummy_vreg>; + reset-gpios = <&gpio0 54 GPIO_ACTIVE_HIGH>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + + cirrus,boost-ind-nanohenry = <1000>; + cirrus,boost-ctl-millivolt = <10000>; + cirrus,boost-peak-milliamp = <4500>; + cirrus,boost-ctl-select = <0>; + cirrus,classh-wk-fet-delay-ms = <100>; + cirrus,classh-weak-fet-thld-millivolt = <100>; + cirrus,temp-warn-threshold = <1>; + cirrus,multi-amp-mode; + cirrus,irq-drive-select = <1>; + cirrus,irq-gpio-select = <1>; + + cirrus,vpbr-config { + cirrus,vpbr-en = <0>; + cirrus,vpbr-thld = <5>; + cirrus,vpbr-atk-rate = <2>; + cirrus,vpbr-atk-vol = <1>; + cirrus,vpbr-max-attn = <9>; + cirrus,vpbr-wait = <1>; + cirrus,vpbr-rel-rate = <5>; + cirrus,vpbr-mute-en = <0>; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml index 376928d1f64b..c1ef9f308396 100644 --- a/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml @@ -27,6 +27,7 @@ properties: enum: - cirrus,cs42l43 - cirrus,cs42l43b + - cirrus,cs42l44 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/cs35l36.txt b/Documentation/devicetree/bindings/sound/cs35l36.txt deleted file mode 100644 index d34117b8558e..000000000000 --- a/Documentation/devicetree/bindings/sound/cs35l36.txt +++ /dev/null @@ -1,168 +0,0 @@ -CS35L36 Speaker Amplifier - -Required properties: - - - compatible : "cirrus,cs35l36" - - - reg : the I2C address of the device for I2C - - - VA-supply, VP-supply : power supplies for the device, - as covered in - Documentation/devicetree/bindings/regulator/regulator.txt. - - - cirrus,boost-ctl-millivolt : Boost Voltage Value. Configures the boost - converter's output voltage in mV. The range is from 2550mV to 12000mV with - increments of 50mV. - (Default) VP - - - cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA. - Configures the peak current by monitoring the current through the boost FET. - Range starts at 1600mA and goes to a maximum of 4500mA with increments of - 50mA. - (Default) 4.50 Amps - - - cirrus,boost-ind-nanohenry : Inductor estimation LBST reference value. - Seeds the digital boost converter's inductor estimation block with the initial - inductance value to reference. - - 1000 = 1uH (Default) - 1200 = 1.2uH - -Optional properties: - - cirrus,multi-amp-mode : Boolean to determine if there are more than - one amplifier in the system. If more than one it is best to Hi-Z the ASP - port to prevent bus contention on the output signal - - - cirrus,boost-ctl-select : Boost converter control source selection. - Selects the source of the BST_CTL target VBST voltage for the boost - converter to generate. - 0x00 - Control Port Value - 0x01 - Class H Tracking (Default) - 0x10 - MultiDevice Sync Value - - - cirrus,amp-pcm-inv : Boolean to determine Amplifier will invert incoming - PCM data - - - cirrus,imon-pol-inv : Boolean to determine Amplifier will invert the - polarity of outbound IMON feedback data - - - cirrus,vmon-pol-inv : Boolean to determine Amplifier will invert the - polarity of outbound VMON feedback data - - - cirrus,dcm-mode-enable : Boost converter automatic DCM Mode enable. - This enables the digital boost converter to operate in a low power - (Discontinuous Conduction) mode during low loading conditions. - - - cirrus,weak-fet-disable : Boolean : The strength of the output drivers is - reduced when operating in a Weak-FET Drive Mode and must not be used to drive - a large load. - - - cirrus,classh-wk-fet-delay : Weak-FET entry delay. Controls the delay - (in ms) before the Class H algorithm switches to the weak-FET voltage - (after the audio falls and remains below the value specified in WKFET_AMP_THLD). - - 0 = 0ms - 1 = 5ms - 2 = 10ms - 3 = 50ms - 4 = 100ms (Default) - 5 = 200ms - 6 = 500ms - 7 = 1000ms - - - cirrus,classh-weak-fet-thld-millivolt : Weak-FET amplifier drive threshold. - Configures the signal threshold at which the PWM output stage enters - weak-FET operation. The range is 50mV to 700mV in 50mV increments. - - - cirrus,temp-warn-threshold : Amplifier overtemperature warning threshold. - Configures the threshold at which the overtemperature warning condition occurs. - When the threshold is met, the overtemperature warning attenuation is applied - and the TEMP_WARN_EINT interrupt status bit is set. - If TEMP_WARN_MASK = 0, INTb is asserted. - - 0 = 105C - 1 = 115C - 2 = 125C (Default) - 3 = 135C - - - cirrus,irq-drive-select : Selects the driver type of the selected interrupt - output. - - 0 = Open-drain - 1 = Push-pull (Default) - - - cirrus,irq-gpio-select : Selects the pin to serve as the programmable - interrupt output. - - 0 = PDM_DATA / SWIRE_SD / INT (Default) - 1 = GPIO - -Optional properties for the "cirrus,vpbr-config" Sub-node - - - cirrus,vpbr-en : VBST brownout prevention enable. Configures whether the - VBST brownout prevention algorithm is enabled or disabled. - - 0 = VBST brownout prevention disabled (default) - 1 = VBST brownout prevention enabled - - See Section 7.31.1 VPBR Config for configuration options & further details - - - cirrus,vpbr-thld : Initial VPBR threshold. Configures the VP brownout - threshold voltage - - - cirrus,cirrus,vpbr-atk-rate : Attenuation attack step rate. Configures the - amount delay between consecutive volume attenuation steps when a brownout - condition is present and the VP brownout condition is in an attacking state. - - - cirrus,vpbr-atk-vol : VP brownout prevention step size. Configures the VP - brownout prevention attacking attenuation step size when operating in either - digital volume or analog gain modes. - - - cirrus,vpbr-max-attn : Maximum attenuation that the VP brownout prevention - can apply to the audio signal. - - - cirrus,vpbr-wait : Configures the delay time between a brownout condition - no longer being present and the VP brownout prevention entering an attenuation - release state. - - - cirrus,vpbr-rel-rate : Attenuation release step rate. Configures the delay - between consecutive volume attenuation release steps when a brownout condition - is not longer present and the VP brownout is in an attenuation release state. - - - cirrus,vpbr-mute-en : During the attack state, if the vpbr-max-attn value - is reached, the error condition still remains, and this bit is set, the audio - is muted. - -Example: - -cs35l36: cs35l36@40 { - compatible = "cirrus,cs35l36"; - reg = <0x40>; - VA-supply = <&dummy_vreg>; - VP-supply = <&dummy_vreg>; - reset-gpios = <&gpio0 54 0>; - interrupt-parent = <&gpio8>; - interrupts = <3 IRQ_TYPE_LEVEL_LOW>; - - cirrus,boost-ind-nanohenry = <1000>; - cirrus,boost-ctl-millivolt = <10000>; - cirrus,boost-peak-milliamp = <4500>; - cirrus,boost-ctl-select = <0x00>; - cirrus,weak-fet-delay = <0x04>; - cirrus,weak-fet-thld = <0x01>; - cirrus,temp-warn-threshold = <0x01>; - cirrus,multi-amp-mode; - cirrus,irq-drive-select = <0x01>; - cirrus,irq-gpio-select = <0x01>; - - cirrus,vpbr-config { - cirrus,vpbr-en = <0x00>; - cirrus,vpbr-thld = <0x05>; - cirrus,vpbr-atk-rate = <0x02>; - cirrus,vpbr-atk-vol = <0x01>; - cirrus,vpbr-max-attn = <0x09>; - cirrus,vpbr-wait = <0x01>; - cirrus,vpbr-rel-rate = <0x05>; - cirrus,vpbr-mute-en = <0x00>; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/dmic-codec.yaml b/Documentation/devicetree/bindings/sound/dmic-codec.yaml index cc3c84dd4c26..83c23e029ea4 100644 --- a/Documentation/devicetree/bindings/sound/dmic-codec.yaml +++ b/Documentation/devicetree/bindings/sound/dmic-codec.yaml @@ -39,6 +39,10 @@ properties: wakeup-delay-ms: description: Delay (in ms) after enabling the DMIC + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + required: - compatible diff --git a/Documentation/devicetree/bindings/sound/eukrea,asoc-tlv320.yaml b/Documentation/devicetree/bindings/sound/eukrea,asoc-tlv320.yaml new file mode 100644 index 000000000000..24b793feb02d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/eukrea,asoc-tlv320.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/eukrea,asoc-tlv320.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Audio complex for Eukrea boards with tlv320aic23 codec. + +maintainers: + - Frank Li <Frank.Li@nxp.com> + +properties: + compatible: + const: eukrea,asoc-tlv320 + + eukrea,model: + $ref: /schemas/types.yaml#/definitions/string + description: + The user-visible name of this sound complex. + + ssi-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: + The phandle of the SSI controller. + + fsl,mux-int-port: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + The internal port of the i.MX audio muxer (AUDMUX). + Note: The AUDMUX port numbering should start at 1, which is consistent with + hardware manual. + minimum: 1 + maximum: 8 + + fsl,mux-ext-port: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + The external port of the i.MX audio muxer. + Note: The AUDMUX port numbering should start at 1, which is consistent with + hardware manual. + minimum: 1 + maximum: 8 + +required: + - compatible + - eukrea,model + - ssi-controller + - fsl,mux-int-port + - fsl,mux-ext-port + +additionalProperties: false + +examples: + - | + sound { + compatible = "eukrea,asoc-tlv320"; + eukrea,model = "imx51-eukrea-tlv320aic23"; + ssi-controller = <&ssi2>; + fsl,mux-int-port = <2>; + fsl,mux-ext-port = <3>; + }; diff --git a/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt b/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt deleted file mode 100644 index 6dfa88c4dc1e..000000000000 --- a/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt +++ /dev/null @@ -1,26 +0,0 @@ -Audio complex for Eukrea boards with tlv320aic23 codec. - -Required properties: - - - compatible : "eukrea,asoc-tlv320" - - - eukrea,model : The user-visible name of this sound complex. - - - ssi-controller : The phandle of the SSI controller. - - - fsl,mux-int-port : The internal port of the i.MX audio muxer (AUDMUX). - - - fsl,mux-ext-port : The external port of the i.MX audio muxer. - -Note: The AUDMUX port numbering should start at 1, which is consistent with -hardware manual. - -Example: - - sound { - compatible = "eukrea,asoc-tlv320"; - eukrea,model = "imx51-eukrea-tlv320aic23"; - ssi-controller = <&ssi2>; - fsl,mux-int-port = <2>; - fsl,mux-ext-port = <3>; - }; diff --git a/Documentation/devicetree/bindings/sound/everest,es8316.yaml b/Documentation/devicetree/bindings/sound/everest,es8316.yaml index fe5d938ca310..f4ff23120c5b 100644 --- a/Documentation/devicetree/bindings/sound/everest,es8316.yaml +++ b/Documentation/devicetree/bindings/sound/everest,es8316.yaml @@ -30,6 +30,18 @@ description: | allOf: - $ref: dai-common.yaml# + - if: + properties: + compatible: + not: + contains: + const: everest,es8316 + then: + properties: + avdd-supply: false + cpvdd-supply: false + dvdd-supply: false + pvdd-supply: false properties: compatible: @@ -49,6 +61,18 @@ properties: items: - const: mclk + avdd-supply: + description: Regulator providing the analog supply, from 2.0 V to 3.6 V + + cpvdd-supply: + description: Regulator providing the charge pump supply, from 1.6 V to 2.0 V + + dvdd-supply: + description: Regulator providing the digital core supply, from 1.6 V to 3.6 V + + pvdd-supply: + description: Regulator providing the digital I/O supply, from 1.6 V to 3.6 V + interrupts: maxItems: 1 description: Headphone detect interrupt @@ -77,6 +101,10 @@ examples: reg = <0x11>; clocks = <&clks 10>; clock-names = "mclk"; + avdd-supply = <®_3p3v>; + cpvdd-supply = <®_1p8v>; + dvdd-supply = <®_1p8v>; + pvdd-supply = <®_1p8v>; #sound-dai-cells = <0>; }; }; diff --git a/Documentation/devicetree/bindings/sound/fsl,micfil.yaml b/Documentation/devicetree/bindings/sound/fsl,micfil.yaml index c47b7a097490..4c7dadb310de 100644 --- a/Documentation/devicetree/bindings/sound/fsl,micfil.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,micfil.yaml @@ -66,6 +66,10 @@ properties: "#sound-dai-cells": const: 0 + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml index 61e8babed402..dc7f4afbb777 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml @@ -8,19 +8,22 @@ title: Loongson 7axxx/2kxxx ASoC audio sound card driver maintainers: - Yingkun Meng <mengyingkun@loongson.cn> + - Binbin Zhou <zhoubinbin@loongson.cn> description: The binding describes the sound card present in loongson 7axxx/2kxxx platform. The sound card is an ASoC component which uses Loongson I2S controller to transfer the audio data. +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: - const: loongson,ls-audio-card - - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name + enum: + - loongson,ls-audio-card # Loongson-2K1000/Loongson-2K2000/LS7A + - loongson,ls2k0300-forever-pi-audio-card # CTCISZ Forever Pi + - loongson,ls2k0300-dl2k0300b-audio-card # ATK-DL2K0300B mclk-fs: $ref: simple-card.yaml#/definitions/mclk-fs @@ -45,14 +48,25 @@ properties: required: - sound-dai + spkr-en-gpios: + maxItems: 1 + description: The GPIO that enables the speakers + + hp-ctl-gpios: + maxItems: 1 + description: The GPIO that control the headphones + + hp-det-gpios: + maxItems: 1 + description: The GPIO that detect headphones are plugged in + required: - compatible - - model - mclk-fs - cpu - codec -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -68,3 +82,28 @@ examples: sound-dai = <&es8323>; }; }; + + - | + #include <dt-bindings/gpio/gpio.h> + + sound { + compatible = "loongson,ls2k0300-dl2k0300b-audio-card"; + model = "loongson-audio"; + mclk-fs = <512>; + hp-det-gpios = <&gpio 81 GPIO_ACTIVE_HIGH>; + spkr-en-gpios = <&gpio 86 GPIO_ACTIVE_HIGH>; + hp-ctl-gpios = <&gpio 87 GPIO_ACTIVE_HIGH>; + audio-routing = + "Headphone", "LOUT1", + "Headphone", "ROUT1", + "Speaker", "LOUT2", + "Speaker", "ROUT2"; + + cpu { + sound-dai = <&i2s>; + }; + + codec { + sound-dai = <&es8388>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml b/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml index da79510bb2d9..51e23c189f7a 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml @@ -14,9 +14,12 @@ allOf: properties: compatible: - const: loongson,ls2k1000-i2s + enum: + - loongson,ls2k0300-i2s + - loongson,ls2k1000-i2s reg: + minItems: 1 items: - description: Loongson I2S controller Registers. - description: APB DMA config register for Loongson I2S controller. @@ -49,6 +52,23 @@ required: unevaluatedProperties: false +if: + properties: + compatible: + contains: + enum: + - loongson,ls2k1000-i2s + +then: + properties: + reg: + minItems: 2 + +else: + properties: + reg: + maxItems: 1 + examples: - | #include <dt-bindings/clock/loongson,ls2k-clk.h> diff --git a/Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml b/Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml new file mode 100644 index 000000000000..1b7451655476 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mtk-btcvsd-snd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek ALSA BT SCO CVSD/MSBC + +maintainers: + - Luca Leonardo Scorcia <l.scorcia@gmail.com> + +properties: + compatible: + const: mediatek,mtk-btcvsd-snd + + reg: + items: + - description: PKV region + - description: SRAM_BANK2 region + + interrupts: + items: + - description: BT-SCO interrupt + + mediatek,infracfg: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the infracfg controller + + mediatek,offset: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Array of register offsets and masks + items: + - description: infra_misc_offset + - description: infra_conn_bt_cvsd_mask + - description: cvsd_mcu_read_offset + - description: cvsd_mcu_write_offset + - description: cvsd_packet_indicator_offset + +required: + - compatible + - reg + - interrupts + - mediatek,infracfg + - mediatek,offset + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + mtk-btcvsd-snd@18000000 { + compatible = "mediatek,mtk-btcvsd-snd"; + reg = <0x18000000 0x1000>, + <0x18080000 0x8000>; + interrupts = <GIC_SPI 286 IRQ_TYPE_LEVEL_LOW>; + mediatek,infracfg = <&infrasys>; + mediatek,offset = <0xf00 0x800 0xfd0 0xfd4 0xfd8>; + }; diff --git a/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt b/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt deleted file mode 100644 index 679e44839b48..000000000000 --- a/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt +++ /dev/null @@ -1,24 +0,0 @@ -Mediatek ALSA BT SCO CVSD/MSBC Driver - -Required properties: -- compatible = "mediatek,mtk-btcvsd-snd"; -- reg: register location and size of PKV and SRAM_BANK2 -- interrupts: should contain BTSCO interrupt -- mediatek,infracfg: the phandles of INFRASYS -- mediatek,offset: Array contains of register offset and mask - infra_misc_offset, - infra_conn_bt_cvsd_mask, - cvsd_mcu_read_offset, - cvsd_mcu_write_offset, - cvsd_packet_indicator_offset - -Example: - - mtk-btcvsd-snd@18000000 { - compatible = "mediatek,mtk-btcvsd-snd"; - reg=<0 0x18000000 0 0x1000>, - <0 0x18080000 0 0x8000>; - interrupts = <GIC_SPI 286 IRQ_TYPE_LEVEL_LOW>; - mediatek,infracfg = <&infrasys>; - mediatek,offset = <0xf00 0x800 0xfd0 0xfd4 0xfd8>; - }; diff --git a/Documentation/devicetree/bindings/sound/neofidelity,ntp8918.yaml b/Documentation/devicetree/bindings/sound/neofidelity,ntp8918.yaml index 6946177e391a..99b739595ebf 100644 --- a/Documentation/devicetree/bindings/sound/neofidelity,ntp8918.yaml +++ b/Documentation/devicetree/bindings/sound/neofidelity,ntp8918.yaml @@ -66,7 +66,7 @@ examples: #sound-dai-cells = <0>; reg = <0x2a>; clocks = <&clkc 150>, <&clkc 151>, <&clkc 152>; - clock-names = "wck", "scl", "bck"; + clock-names = "wck", "scl", "bck"; reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>; }; }; diff --git a/Documentation/devicetree/bindings/sound/omap-dmic.txt b/Documentation/devicetree/bindings/sound/omap-dmic.txt deleted file mode 100644 index 418e30e72e89..000000000000 --- a/Documentation/devicetree/bindings/sound/omap-dmic.txt +++ /dev/null @@ -1,20 +0,0 @@ -* Texas Instruments OMAP4+ Digital Microphone Module - -Required properties: -- compatible: "ti,omap4-dmic" -- reg: Register location and size as an array: - <MPU access base address, size>, - <L3 interconnect address, size>; -- interrupts: Interrupt number for DMIC -- ti,hwmods: Name of the hwmod associated with OMAP dmic IP - -Example: - -dmic: dmic@4012e000 { - compatible = "ti,omap4-dmic"; - reg = <0x4012e000 0x7f>, /* MPU private access */ - <0x4902e000 0x7f>; /* L3 Interconnect */ - interrupts = <0 114 0x4>; - interrupt-parent = <&gic>; - ti,hwmods = "dmic"; -}; diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml index a899c4e7c1c9..33bc23b6176a 100644 --- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml @@ -14,7 +14,13 @@ description: properties: compatible: - const: qcom,msm8916-wcd-digital-codec + oneOf: + - items: + - enum: + - qcom,sdm660-wcd-digital-codec + - const: qcom,msm8916-wcd-digital-codec + + - const: qcom,msm8916-wcd-digital-codec reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml index 94e7a1860977..be47dbdb2e92 100644 --- a/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml @@ -14,7 +14,16 @@ description: properties: compatible: - const: qcom,pm8916-wcd-analog-codec + oneOf: + - items: + - enum: + - qcom,pm660l-wcd-analog-codec + - const: qcom,pm8953-wcd-analog-codec + + - enum: + - qcom,pm8916-wcd-analog-codec + - qcom,pm8950-wcd-analog-codec + - qcom,pm8953-wcd-analog-codec reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml index 2fb95544db8b..4878c424ef03 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml @@ -21,6 +21,43 @@ properties: '#sound-dai-cells': const: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +# Digital Audio Interfaces +patternProperties: + '^dai@[0-9a-f]+$': + type: object + description: + Q6DSP Digital Audio Interfaces. + + properties: + reg: + maxItems: 1 + description: + Digital Audio Interface ID + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + items: + - enum: [bclk, mclk] + - const: mclk + + dependencies: + clocks: [clock-names] + + required: + - reg + + additionalProperties: false + required: - compatible - '#sound-dai-cells' @@ -29,7 +66,20 @@ unevaluatedProperties: false examples: - | + #include <dt-bindings/sound/qcom,q6afe.h> + dais { compatible = "qcom,q6apm-lpass-dais"; #sound-dai-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + dai@10 { + reg = <PRIMARY_MI2S_RX>; + clocks = <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT + LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_CLK_ID_MCLK_1 + LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk", "mclk"; + }; }; diff --git a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml index 2b27d6c8f58f..3b03e2acd67e 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml @@ -127,7 +127,7 @@ patternProperties: contains: # MI2S DAI ID range PRIMARY_MI2S_RX - QUATERNARY_MI2S_TX and # QUINARY_MI2S_RX - QUINARY_MI2S_TX and - # LPI_MI2S_RX_0 - SENARY_MI2S_TX + # LPI_MI2S_RX_0 - LPI_MI2S_TX_6 items: oneOf: - minimum: 16 @@ -135,7 +135,7 @@ patternProperties: - minimum: 127 maximum: 128 - minimum: 137 - maximum: 148 + maximum: 152 then: required: - qcom,sd-lines diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index dae440ecab59..1536fcd96d68 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -24,12 +24,16 @@ properties: - items: - enum: - qcom,eliza-sndcard + - qcom,hawi-sndcard - qcom,kaanapali-sndcard + - qcom,maili-sndcard + - qcom,sm8475-sndcard - qcom,sm8550-sndcard - qcom,sm8650-sndcard - qcom,sm8750-sndcard - const: qcom,sm8450-sndcard - enum: + - ayaneo,pocket-s2-sndcard - fairphone,fp4-sndcard - fairphone,fp5-sndcard - qcom,apq8096-sndcard @@ -45,6 +49,7 @@ properties: - qcom,qrb5165-rb5-sndcard - qcom,sc7180-qdsp6-sndcard - qcom,sc8280xp-sndcard + - qcom,sdm660-sndcard - qcom,sdm845-sndcard - qcom,sm8250-sndcard - qcom,sm8450-sndcard @@ -91,6 +96,19 @@ patternProperties: sound-dai: maxItems: 1 + dai-tdm-slot-num: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of slots in use + + dai-tdm-slot-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width, in bits, of each slot + + patternProperties: + '^dai-tdm-slot-[rt]x-mask$': + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Slot mask for active TDM slots + platform: description: Holds subnode which indicates platform dai. type: object @@ -110,6 +128,19 @@ patternProperties: minItems: 1 maxItems: 8 + dai-tdm-slot-num: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of slots in use + + dai-tdm-slot-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width, in bits, of each slot + + patternProperties: + '^dai-tdm-slot-[rt]x-mask$': + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Slot mask for active TDM slots + required: - link-name - cpu diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa8855.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa8855.yaml new file mode 100644 index 000000000000..00a6a05e9443 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wsa8855.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wsa8855.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WSA8855 stereo smart speaker amplifier + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com> + - Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com> + +description: + WSA885X is a Qualcomm Aqstic stereo smart speaker amplifier. It uses a PCM + audio interface, an I2C control interface, and a Class-H amplifier path for + high efficiency, low output noise, and low idle power consumption. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: qcom,wsa8855 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + powerdown-gpios: + description: GPIO controlling the SD_N powerdown pin. + maxItems: 1 + + reset-gpios: + description: GPIO controlling the SD_N powerdown pin. + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-1p8-supply: true + + vdd-io-supply: true + + qcom,battery-config: + description: + Battery topology connected to the speaker amplifier. 1S indicates one + battery cell in series, while 2S indicates two battery cells in series. + $ref: /schemas/types.yaml#/definitions/string + default: 1s + enum: + - 1s + - 2s + +required: + - compatible + - reg + - '#sound-dai-cells' + - interrupts + - vdd-1p8-supply + - vdd-io-supply + +oneOf: + - required: + - powerdown-gpios + - required: + - reset-gpios + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + speaker@c { + compatible = "qcom,wsa8855"; + reg = <0x0c>; + #sound-dai-cells = <0>; + powerdown-gpios = <&tlmm 11 GPIO_ACTIVE_LOW>; + interrupt-parent = <&tlmm>; + interrupts = <77 IRQ_TYPE_EDGE_FALLING>; + vdd-1p8-supply = <&vreg_l2g_1p8>; + vdd-io-supply = <&vreg_l1g_1p2>; + qcom,battery-config = "1s"; + }; + }; +... diff --git a/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml index 880196081a60..3c44ebf93a44 100644 --- a/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml @@ -19,7 +19,7 @@ properties: realtek,power-up-delay-ms: description: Set a delay time for flush work to be completed, - this vlaue is adjustable depending on platform. + this value is adjustable depending on platform. maxItems: 1 required: diff --git a/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml b/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml index d7fa16554698..213c1c40b6f0 100644 --- a/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml @@ -793,7 +793,7 @@ examples: bitclock-master = <&rsnd_endpoint0>; frame-master = <&rsnd_endpoint0>; playback = <&ssi3>, <&src1>, <&dvc1>; - capture = <&ssi4>, <&src0>, <&dvc0>; + capture = <&ssi4>, <&src0>, <&dvc0>; }; }; }; diff --git a/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml b/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml index 240d90402e4f..72723f067b2a 100644 --- a/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml @@ -23,9 +23,9 @@ allOf: then: properties: clocks: - minItems: 7 + minItems: 6 clock-names: - minItems: 7 + minItems: 6 else: properties: clocks: @@ -43,26 +43,50 @@ properties: maxItems: 1 clocks: - minItems: 4 - items: - - description: clock for I2S sysclk - - description: clock for I2S bclk - - description: clock for I2S bus - - description: clock for I2S controller - - description: clock for I2S sysclk divider - - description: clock for I2S common sysclk - - description: clock for I2S common bclk + oneOf: + - items: + - description: clock for I2S sysclk + - description: clock for I2S bclk + - description: clock for I2S bus + - description: clock for I2S controller + - items: + - description: clock for I2S sysclk + - description: clock for I2S bclk + - description: clock for I2S bus + - description: clock for I2S controller + - description: clock for I2S common sysclk + - description: clock for I2S common bclk + - items: + - description: clock for I2S sysclk + - description: clock for I2S bclk + - description: clock for I2S bus + - description: clock for I2S controller + - description: clock for I2S sysclk divider + - description: clock for I2S common sysclk + - description: clock for I2S common bclk clock-names: - minItems: 4 - items: - - const: sysclk - - const: bclk - - const: bus - - const: func - - const: sysclk_div - - const: c_sysclk - - const: c_bclk + oneOf: + - items: + - const: sysclk + - const: bclk + - const: bus + - const: func + - items: + - const: sysclk + - const: bclk + - const: bus + - const: func + - const: c_sysclk + - const: c_bclk + - items: + - const: sysclk + - const: bclk + - const: bus + - const: func + - const: sysclk_div + - const: c_sysclk + - const: c_bclk dmas: minItems: 1 diff --git a/Documentation/devicetree/bindings/sound/st,sta32x.txt b/Documentation/devicetree/bindings/sound/st,sta32x.txt index 52265fb757c5..266b6fc9f009 100644 --- a/Documentation/devicetree/bindings/sound/st,sta32x.txt +++ b/Documentation/devicetree/bindings/sound/st,sta32x.txt @@ -73,7 +73,7 @@ Optional properties: - st,odd-pwm-speed-mode: If present, PWM speed mode run on odd speed mode (341.3 kHz) on all - channels. If not present, normal PWM spped mode (384 kHz) will be used. + channels. If not present, normal PWM speed mode (384 kHz) will be used. - st,invalid-input-detect-mute: If present, automatic invalid input detect mute is enabled. diff --git a/Documentation/devicetree/bindings/sound/ti,omap4-dmic.yaml b/Documentation/devicetree/bindings/sound/ti,omap4-dmic.yaml new file mode 100644 index 000000000000..773b9bf2f1c2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,omap4-dmic.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,omap4-dmic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OMAP4+ Digital Microphone Module + +maintainers: + - Peter Ujfalusi <peter.ujfalusi@ti.com> + +properties: + compatible: + const: ti,omap4-dmic + + reg: + items: + - description: MPU access base address + - description: L3 interconnect address + + reg-names: + items: + - const: mpu + - const: dma + + interrupts: + maxItems: 1 + + dmas: + maxItems: 1 + + dma-names: + items: + - const: up_link + +required: + - compatible + - reg + - reg-names + - interrupts + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + dmic@0 { + compatible = "ti,omap4-dmic"; + reg = <0x0 0x7f>, /* MPU private access */ + <0x4902e000 0x7f>; /* L3 Interconnect */ + reg-names = "mpu", "dma"; + interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&sdma 67>; + dma-names = "up_link"; + }; diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml index 4d951ece394e..dffc41d5f7de 100644 --- a/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml +++ b/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml @@ -24,6 +24,10 @@ properties: description: a GPIO spec for the MUTE pin. + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + required: - compatible - wlf,mute-gpios diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c index 5167305521cd..c966b30e1082 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c +++ b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c @@ -147,7 +147,7 @@ static const struct reg_default halo_register_defaults[] = { { 0x2b805c0, 0 }, /* HALO_SCRATCH1 */ { 0x2b805c8, 0 }, /* HALO_SCRATCH2 */ { 0x2b805d0, 0 }, /* HALO_SCRATCH3 */ - { 0x2b805c8, 0 }, /* HALO_SCRATCH4 */ + { 0x2b805d8, 0 }, /* HALO_SCRATCH4 */ { 0x2bc1000, 0 }, /* HALO_CCM_CORE_CONTROL */ { 0x2bc7000, 0 }, /* HALO_WDT_CONTROL */ diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index e73c1bea9059..d61a97c5b41e 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -105,6 +105,9 @@ static int sdw_bus_probe(struct device *dev) } slave->index = ret; + /* Create IRQ mapping now so the driver can get it in probe() */ + sdw_irq_create_mapping(slave); + ret = drv->probe(slave, id); if (ret) { ida_free(&slave->bus->slave_ida, slave->index); @@ -117,9 +120,6 @@ static int sdw_bus_probe(struct device *dev) if (drv->ops && drv->ops->read_prop) drv->ops->read_prop(slave); - if (slave->prop.use_domain_irq) - sdw_irq_create_mapping(slave); - /* init the dynamic sysfs attributes we need */ ret = sdw_slave_sysfs_dpn_init(slave); if (ret < 0) diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 45850f2d4342..2e879a3c09ae 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -152,6 +152,10 @@ #define LPI_MI2S_TX_4 146 #define SENARY_MI2S_RX 147 #define SENARY_MI2S_TX 148 +#define LPI_MI2S_RX_5 149 +#define LPI_MI2S_TX_5 150 +#define LPI_MI2S_RX_6 151 +#define LPI_MI2S_TX_6 152 #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 @@ -233,11 +237,39 @@ /* Clock ID for RX CORE MCLK2 2X MCLK */ #define LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 70 +#define LAPSS_CLK_ID_QAIF_IF0_IBIT 71 +#define LAPSS_CLK_ID_QAIF_IF0_EBIT 72 +#define LAPSS_CLK_ID_QAIF_IF1_IBIT 73 +#define LAPSS_CLK_ID_QAIF_IF1_EBIT 74 +#define LAPSS_CLK_ID_QAIF_IF2_IBIT 75 +#define LAPSS_CLK_ID_QAIF_IF2_EBIT 76 +#define LAPSS_CLK_ID_QAIF_IF3_IBIT 77 +#define LAPSS_CLK_ID_QAIF_IF3_EBIT 78 +#define LAPSS_CLK_ID_QAIF_IF4_IBIT 79 +#define LAPSS_CLK_ID_QAIF_IF4_EBIT 80 +#define LAPSS_CLK_ID_QAIF_IF5_IBIT 81 +#define LAPSS_CLK_ID_QAIF_IF5_EBIT 82 +#define LAPSS_CLK_ID_QAIF_IF6_IBIT 83 +#define LAPSS_CLK_ID_QAIF_IF6_EBIT 84 +#define LAPSS_CLK_ID_QAIF_IF7_IBIT 85 +#define LAPSS_CLK_ID_QAIF_IF7_EBIT 86 +#define LAPSS_CLK_ID_QAIF_IF8_IBIT 87 +#define LAPSS_CLK_ID_QAIF_IF8_EBIT 88 +#define LAPSS_CLK_ID_QAIF_IF9_IBIT 89 +#define LAPSS_CLK_ID_QAIF_IF9_EBIT 90 +#define LAPSS_CLK_ID_QAIF_IF10_IBIT 91 +#define LAPSS_CLK_ID_QAIF_IF10_EBIT 92 +#define LAPSS_CLK_ID_QAIF_IF11_IBIT 93 +#define LAPSS_CLK_ID_QAIF_IF11_EBIT 94 +#define LAPSS_CLK_ID_QAIF_IF12_IBIT 95 +#define LAPSS_CLK_ID_QAIF_IF12_EBIT 96 +#define LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 97 +#define LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 98 + #define LPASS_HW_AVTIMER_VOTE 101 #define LPASS_HW_MACRO_VOTE 102 #define LPASS_HW_DCODEC_VOTE 103 - -#define Q6AFE_MAX_CLK_ID 104 +#define LPASS_HW_LPR_VOTE 104 #define LPASS_CLK_ATTRIBUTE_INVALID 0x0 #define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 diff --git a/include/linux/platform_data/omap-twl4030.h b/include/linux/platform_data/omap-twl4030.h deleted file mode 100644 index 7fcb55fe21c9..000000000000 --- a/include/linux/platform_data/omap-twl4030.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/** - * omap-twl4030.h - ASoC machine driver for TI SoC based boards with twl4030 - * codec, header. - * - * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com - * All rights reserved. - * - * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> - */ - -#ifndef _OMAP_TWL4030_H_ -#define _OMAP_TWL4030_H_ - -/* To select if only one channel is connected in a stereo port */ -#define OMAP_TWL4030_LEFT (1 << 0) -#define OMAP_TWL4030_RIGHT (1 << 1) - -struct omap_tw4030_pdata { - const char *card_name; - /* Voice port is connected to McBSP3 */ - bool voice_connected; - - /* The driver will parse the connection flags if this flag is set */ - bool custom_routing; - /* Flags to indicate connected audio ports. */ - u8 has_hs; - u8 has_hf; - u8 has_predriv; - u8 has_carkit; - bool has_ear; - - bool has_mainmic; - bool has_submic; - bool has_hsmic; - bool has_carkitmic; - bool has_digimic0; - bool has_digimic1; - u8 has_linein; -}; - -#endif /* _OMAP_TWL4030_H_ */ diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 2490b72c0a7a..45a5df574aa6 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -417,7 +417,6 @@ void cs35l56_wait_control_port_ready(void); void cs35l56_wait_min_reset_pulse(void); void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire); int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); -irqreturn_t cs35l56_irq(int irq, void *data); int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base); int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base); int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 9472f0a966a2..81422219ed39 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -175,12 +175,6 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, struct dmaengine_pcm { struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1]; const struct snd_dmaengine_pcm_config *config; - struct snd_soc_component component; unsigned int flags; }; - -static inline struct dmaengine_pcm *soc_component_to_pcm(struct snd_soc_component *p) -{ - return container_of(p, struct dmaengine_pcm, component); -} #endif diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index fed69998c93f..ba35f03576b9 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -9,6 +9,22 @@ struct hdac_bus; struct hdac_ext_link; +/** + * enum hda_bus_ml_link_type - mlink link type, used by SOF link DMA + * allocator constraints (see struct sof_intel_hda_dev). + * + * @HDA_BUS_ML_LINK_HDA: non-alt link, i.e. HDA codec or iDisp + * @HDA_BUS_ML_LINK_SDW: alt link, SoundWire + * @HDA_BUS_ML_LINK_UAOL: alt link, USB Audio Offload + * @HDA_BUS_ML_LINK_OTHER: alt link, SSP or DMIC + */ +enum hda_bus_ml_link_type { + HDA_BUS_ML_LINK_HDA, + HDA_BUS_ML_LINK_SDW, + HDA_BUS_ML_LINK_UAOL, + HDA_BUS_ML_LINK_OTHER, +}; + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) int hda_bus_ml_init(struct hdac_bus *bus); @@ -49,11 +65,12 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, int channel_mask, int stream_id, int dir); -void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); int hda_bus_ml_suspend(struct hdac_bus *bus); +enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink); + struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus); @@ -169,11 +186,13 @@ hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, return 0; } -static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } +static inline enum hda_bus_ml_link_type +hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) { return HDA_BUS_ML_LINK_HDA; } + static inline struct hdac_ext_link * hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index ca35d5a44370..599a72f33515 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -25,6 +25,8 @@ struct snd_soc_dai_driver; struct snd_soc_dai_ops; struct snd_soc_dapm_route; struct snd_soc_dapm_widget; +struct snd_soc_pcm_stream; +struct sdca_entity; /* convenient macro to handle the mono volume in 7.8 fixed format representation */ #define SDCA_SINGLE_Q78_TLV(xname, xreg, xmin, xmax, xstep, tlv_array) \ @@ -82,6 +84,11 @@ int sdca_asoc_populate_component(struct device *dev, struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, const struct snd_soc_dai_ops *ops); +int sdca_asoc_populate_rate_format(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_pcm_stream *stream); + int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, struct sdca_function_data *function, struct snd_pcm_substream *substream, @@ -100,9 +107,9 @@ int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, +int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, int function_id, int entity_id, - int from_ps, int to_ps, - const struct sdca_pde_delay *pde_delays, - int num_delays); + int from_ps, int to_ps, + const struct sdca_pde_delay *pde_delays, + int num_delays); #endif // __SDCA_ASOC_H__ diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h index dc33927b82bd..bc3600cdd2ec 100644 --- a/include/sound/sdca_fdl.h +++ b/include/sound/sdca_fdl.h @@ -83,6 +83,10 @@ static inline int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) return 0; } +static inline void sdca_fdl_free_state(struct sdca_interrupt *interrupt) +{ +} + static inline int sdca_fdl_process(struct sdca_interrupt *interrupt) { return 0; diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index b1489178b0ef..e580201d69d5 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -298,7 +298,7 @@ enum sdca_xu_controls { }; /** - * enum sdca_set_index_range - Column definitions UMP SetIndex + * enum sdca_fdl_set_index_range - Column definitions UMP SetIndex */ enum sdca_fdl_set_index_range { SDCA_FDL_SET_INDEX_SET_NUMBER = 0, @@ -803,6 +803,8 @@ struct sdca_control_range { * @mode: Access mode of the Control. * @layers: Bitmask of access layers of the Control. * @deferrable: Indicates if the access to the Control can be deferred. + * @is_volatile: Indicates the Control registers are forced to be treated + * as volatile. * @has_default: Indicates the Control has a default value to be written. * @has_reset: Indicates the Control has a defined reset value. * @has_fixed: Indicates the Control only supports a single value. @@ -1116,7 +1118,6 @@ struct sdca_entity_ge { /** * struct sdca_entity_hide - information specific to HIDE Entities - * @hid: HID device structure * @num_hidtx_ids: number of HIDTx Report ID * @num_hidrx_ids: number of HIDRx Report ID * @hidtx_ids: HIDTx Report ID @@ -1127,11 +1128,8 @@ struct sdca_entity_ge { * within this Device * @max_delay: the maximum time in microseconds allowed for the Device * to change the ownership from Device to Host - * @hid_report_desc: HID Report Descriptor for the HIDE Entity - * @hid_desc: HID descriptor for the HIDE Entity */ struct sdca_entity_hide { - struct hid_device *hid; unsigned int *hidtx_ids; unsigned int *hidrx_ids; int num_hidtx_ids; @@ -1139,12 +1137,10 @@ struct sdca_entity_hide { unsigned int af_number_list[SDCA_MAX_FUNCTION_COUNT]; unsigned int hide_reside_function_num; unsigned int max_delay; - unsigned char *hid_report_desc; - struct hid_descriptor hid_desc; }; /** - * enum sdca_xu_reset_machanism - SDCA FDL Resets + * enum sdca_xu_reset_mechanism - SDCA FDL Resets */ enum sdca_xu_reset_mechanism { SDCA_XU_RESET_FUNCTION = 0x0, @@ -1402,6 +1398,16 @@ struct sdca_fdl_data { }; /** + * struct sdca_function_hid - information about a function's HID descriptors + * @report_desc: HID Report Descriptor for the HID Function + * @desc: HID descriptor for the HID Function + */ +struct sdca_function_hid { + unsigned char *report_desc; + struct hid_descriptor desc; +}; + +/** * struct sdca_function_data - top-level information for one SDCA function * @desc: Pointer to short descriptor from initial parsing. * @init_table: Pointer to a table of initialization writes. @@ -1415,6 +1421,7 @@ struct sdca_fdl_data { * @reset_max_delay: Maximum Function reset delay in microseconds, before an * error should be reported. * @fdl_data: FDL data for this Function, if available. + * @hid: HID data for this Function, if available. */ struct sdca_function_data { struct sdca_function_desc *desc; @@ -1430,6 +1437,10 @@ struct sdca_function_data { unsigned int reset_max_delay; struct sdca_fdl_data fdl_data; + + union { + struct sdca_function_hid hid; + }; }; static inline u32 sdca_range(struct sdca_control_range *range, @@ -1451,8 +1462,7 @@ static inline u32 sdca_range_search(struct sdca_control_range *range, return 0; } -int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_data *function); +int sdca_parse_function(struct device *dev, struct sdca_function_data *function); const char *sdca_find_terminal_name(enum sdca_terminal_type type); @@ -1469,5 +1479,7 @@ struct sdca_control_range *sdca_selector_find_range(struct device *dev, struct sdca_cluster *sdca_id_find_cluster(struct device *dev, struct sdca_function_data *function, const int id); +struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, + const char *entity_label); #endif diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h index 18bebbe428c9..848081df3e4e 100644 --- a/include/sound/sdca_hid.h +++ b/include/sound/sdca_hid.h @@ -16,18 +16,21 @@ struct sdca_interrupt; #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) -int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - struct sdca_entity *entity); +int sdca_add_hid_device(struct sdca_interrupt *interrupt); +void sdca_destroy_hid_device(struct sdca_interrupt *interrupt); int sdca_hid_process_report(struct sdca_interrupt *interrupt); #else -static inline int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - struct sdca_entity *entity) +static inline int sdca_add_hid_device(struct sdca_interrupt *interrupt) { return 0; } +static inline void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) +{ +} + static inline int sdca_hid_process_report(struct sdca_interrupt *interrupt) { return 0; diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 915e6ae5f68d..bd8c3a033577 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -181,7 +181,7 @@ void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platfo void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, int is_single_links); -void simple_util_clean_reference(struct snd_soc_card *card); +void simple_util_clean_reference(struct simple_util_priv *priv); void simple_util_parse_convert(struct device_node *np, char *prefix, struct simple_util_data *data); @@ -189,18 +189,37 @@ bool simple_util_is_convert_required(const struct simple_util_data *data); int simple_util_get_sample_fmt(struct simple_util_data *data); -int simple_util_parse_routing(struct snd_soc_card *card, - char *prefix); -int simple_util_parse_widgets(struct snd_soc_card *card, - char *prefix); -int simple_util_parse_pin_switches(struct snd_soc_card *card, - char *prefix); +int simple_util_parse_property(struct simple_util_priv *priv, + int (*func)(struct snd_soc_card *card, const char *propname), + char *prefix, char *property); +static inline int simple_util_parse_routing(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_audio_routing, + prefix, "routing"); +} + +static inline int simple_util_parse_widgets(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_audio_simple_widgets, + prefix, "widgets"); +} + +static inline int simple_util_parse_pin_switches(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_pin_switches, + prefix, "pin-switches"); +} + +static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_aux_devs, + prefix, "aux-devs"); +} int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, int is_hp, char *prefix, char *pin); -int simple_util_init_aux_jacks(struct simple_util_priv *priv, - char *prefix); +int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix); int simple_util_init_priv(struct simple_util_priv *priv, struct link_info *li); void simple_util_remove(struct platform_device *pdev); diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 0519afd7217f..2ee303b791dc 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -57,7 +57,7 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) #endif /** - * snd_soc_acpi_mach_params: interface for machine driver configuration + * struct snd_soc_acpi_mach_params - interface for machine driver configuration * * @acpi_ipc_irq_index: used for BYT-CR detection * @platform: string used for HDAudio codec support @@ -93,7 +93,7 @@ struct snd_soc_acpi_mach_params { }; /** - * snd_soc_acpi_endpoint - endpoint descriptor + * struct snd_soc_acpi_endpoint - endpoint descriptor * @num: endpoint number (mandatory, unique per device) * @aggregated: 0 (independent) or 1 (logically grouped) * @group_position: zero-based order (only when @aggregated is 1) @@ -107,7 +107,7 @@ struct snd_soc_acpi_endpoint { }; /** - * snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device + * struct snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device * @adr: 64 bit ACPI _ADR value * @num_endpoints: number of endpoints for this device * @endpoints: array of endpoints @@ -121,7 +121,7 @@ struct snd_soc_acpi_adr_device { }; /** - * snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices + * struct snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices * @mask: one bit set indicates the link this list applies to * @num_adr: ARRAY_SIZE of devices * @adr_d: array of devices @@ -167,8 +167,8 @@ struct snd_soc_acpi_link_adr { #define SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME BIT(4) /** - * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are - * related to the hardware, except for the firmware and topology file names. + * struct snd_soc_acpi_mach - ACPI-based machine descriptor. Most of the fields + * are related to the hardware, except for the firmware and topology file names. * A platform supported by legacy and Sound Open Firmware (SOF) would expose * all firmware/topology related fields. * @@ -192,6 +192,7 @@ struct snd_soc_acpi_link_adr { * the initial selection in the snd_soc_acpi_mach table. * @pdata: intended for platform data or machine specific-ops. This structure * is not constant since this field may be updated at run-time + * @mach_params: machine driver configuration * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled * @tplg_quirk_mask: quirks to select different topology files dynamically * @get_function_tplg_files: This is an optional callback, if specified then instead of @@ -199,11 +200,11 @@ struct snd_soc_acpi_link_adr { * files to be loaded. * Return value: The number of the files or negative ERRNO. 0 means that the single topology * file should be used, no function topology split can be used on the machine. - * @card: the pointer of the card - * @mach: the pointer of the machine driver - * @prefix: the prefix of the topology file name. Typically, it is the path. - * @tplg_files: the pointer of the array of the topology file names. - * @best_effort: ignore non supported links and try to build the card in best effort + * card: the pointer of the card + * mach: the pointer of the machine driver + * prefix: the prefix of the topology file name. Typically, it is the path. + * tplg_files: the pointer of the array of the topology file names. + * best_effort: ignore non supported links and try to build the card in best effort * with supported links */ /* Descriptor for SST ASoC machine driver */ diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 4b7d7954953d..c49b59630101 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -253,6 +253,9 @@ struct snd_soc_component { void *mark_pm; struct dentry *debugfs_root; + + /* Component private data */ + void *priv; }; #define for_each_component_dais(component, dai)\ @@ -283,6 +286,14 @@ static inline int snd_soc_component_cache_sync( return regcache_sync(component->regmap); } +struct snd_soc_component *snd_soc_component_alloc(struct device *dev); + +void snd_soc_component_set_name(struct snd_soc_component *component, const char *name); +const char *snd_soc_component_name(struct snd_soc_component *component); + +void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv); +void *snd_soc_component_to_priv(struct snd_soc_component *component); + void snd_soc_component_set_aux(struct snd_soc_component *component, struct snd_soc_aux_dev *aux); int snd_soc_component_init(struct snd_soc_component *component); diff --git a/include/sound/soc.h b/include/sound/soc.h index 10ad80f930c2..f46b2bc2a022 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -447,15 +447,16 @@ static inline int snd_soc_resume(struct device *dev) } #endif int snd_soc_poweroff(struct device *dev); -int snd_soc_component_initialize(struct snd_soc_component *component, - const struct snd_soc_component_driver *driver, - struct device *dev); -int snd_soc_add_component(struct snd_soc_component *component, - struct snd_soc_dai_driver *dai_drv, - int num_dai); -int snd_soc_register_component(struct device *dev, +int snd_soc_register_component_c(struct snd_soc_component *component, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai); +int snd_soc_register_component_d(struct device *dev, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, int num_dai); +#define snd_soc_register_component(x, ...) _Generic((x), \ +struct device * : snd_soc_register_component_d, \ +struct snd_soc_component * : snd_soc_register_component_c)(x, __VA_ARGS__) + int devm_snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai); diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 79c21966220b..9b28e9aef4f1 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -44,6 +44,18 @@ struct asoc_sdw_codec_info; +struct asoc_sdw_mc_private { + struct snd_soc_card card; + struct snd_soc_jack sdw_headset; + struct device *headset_codec_dev; /* only one headset per card */ + struct device *amp_dev1, *amp_dev2; + bool append_dai_type; + bool ignore_internal_dmic; + void *private; + unsigned long mc_quirk; + int codec_info_list_count; +}; + struct asoc_sdw_dai_info { const bool direction[2]; /* playback & capture support */ const char *codec_name; @@ -88,25 +100,13 @@ struct asoc_sdw_codec_info { int (*codec_card_late_probe)(struct snd_soc_card *card); - int (*count_sidecar)(struct snd_soc_card *card, + int (*count_sidecar)(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int (*add_sidecar)(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, struct snd_soc_codec_conf **codec_conf); }; -struct asoc_sdw_mc_private { - struct snd_soc_card card; - struct snd_soc_jack sdw_headset; - struct device *headset_codec_dev; /* only one headset per card */ - struct device *amp_dev1, *amp_dev2; - bool append_dai_type; - bool ignore_internal_dmic; - void *private; - unsigned long mc_quirk; - int codec_info_list_count; -}; - struct asoc_sdw_endpoint { struct list_head list; @@ -182,7 +182,8 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks const struct snd_soc_acpi_endpoint *new); int asoc_sdw_get_dai_type(u32 type); -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, @@ -235,7 +236,7 @@ int asoc_sdw_es9356_amp_init(struct snd_soc_card *card, int asoc_sdw_es9356_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* CS AMP support */ -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h index 59cd014392c1..e2107da6558f 100644 --- a/include/sound/sof/dai-amd.h +++ b/include/sound/sof/dai-amd.h @@ -18,6 +18,7 @@ struct sof_ipc_dai_acp_params { uint32_t fsync_rate; /* FSYNC frequency in Hz */ uint32_t tdm_slots; uint32_t tdm_mode; + uint32_t format; } __packed; /* ACPDMIC Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 36809f712723..e3fe492e78f5 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -90,6 +90,8 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ + SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ + SOF_DAI_AMD_I2S, /**< AMD ACP I2S */ }; /* general purpose DAI configuration */ @@ -121,6 +123,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_mtk_afe_params afe; struct sof_ipc_dai_micfil_params micfil; struct sof_ipc_dai_acp_sdw_params acp_sdw; + struct sof_ipc_dai_acp_params acp_i2s; }; } __packed; diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index 4e406dc22f31..6c2c3a5fc90d 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -179,7 +179,7 @@ struct sof_ipc_compound_hdr { uint32_t count; /**< count of 0 means end of compound sequence */ } __packed; -/** +/* * OOPS header architecture specific data. */ struct sof_ipc_dsp_oops_arch_hdr { @@ -187,7 +187,7 @@ struct sof_ipc_dsp_oops_arch_hdr { uint32_t totalsize; /* Total size of oops message */ } __packed; -/** +/* * OOPS header platform specific data. */ struct sof_ipc_dsp_oops_plat_hdr { diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 4554e5e8cab5..cea3d9453b84 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -187,6 +187,10 @@ enum sof_ipc4_pipeline_state { #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK GENMASK(23, 20) #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT) +#define SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT 29 +#define SOF_IPC4_GLB_PIPE_PAYLOAD_MASK BIT(29) +#define SOF_IPC4_GLB_PIPE_PAYLOAD(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_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) @@ -536,12 +540,63 @@ enum sof_ipc4_notification_type { SOF_IPC4_NOTIFY_TYPE_LAST, }; +enum sof_ipc4_resource_type { + SOF_IPC4_MODULE_INSTANCE, + SOF_IPC4_PIPELINE, + SOF_IPC4_GATEWAY, + SOF_IPC4_EDF_TASK, + SOF_IPC4_INVALID_RESOURCE_TYPE, +}; + +enum sof_ipc4_event_type { + /* Underrun detected by the Mixer */ + SOF_IPC4_MIXER_UNDERRUN_DETECTED = 1, + /* Error caught during data processing */ + SOF_IPC4_PROCESS_DATA_ERROR = 3, + /* Underrun detected by gateway. */ + SOF_IPC4_GATEWAY_UNDERRUN_DETECTED = 6, + /* Overrun detected by gateway */ + SOF_IPC4_GATEWAY_OVERRUN_DETECTED, +}; + +/** + * struct sof_ipc4_process_data_error_event_data - process data error event payload + * @error_code: Error code returned by data processing function + */ +struct sof_ipc4_process_data_error_event_data { + uint32_t error_code; +}; + +/** + * struct sof_ipc4_mixer_underrun_event_data - mixer underrun event payload + * @eos_flag: Indicates EndOfStream + * @data_mixed: Data processed by module (in bytes) + * @expected_data_mixed: Expected data to be processed (in bytes) + */ +struct sof_ipc4_mixer_underrun_event_data { + uint32_t eos_flag; + uint32_t data_mixed; + uint32_t expected_data_mixed; +}; + +/** + * union sof_ipc4_resource_event_data - resource event specific payload + * @dws: Raw event data payload as six dwords + * @process_data_error: SOF_IPC4_PROCESS_DATA_ERROR payload + * @mixer_underrun: SOF_IPC4_MIXER_UNDERRUN_DETECTED payload + */ +union sof_ipc4_resource_event_data { + uint32_t dws[6]; + struct sof_ipc4_process_data_error_event_data process_data_error; + struct sof_ipc4_mixer_underrun_event_data mixer_underrun; +}; + 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]; + union sof_ipc4_resource_event_data data; } __packed __aligned(4); #define SOF_IPC4_DEBUG_DESCRIPTOR_SIZE 12 /* 3 x u32 */ @@ -654,11 +709,81 @@ enum sof_ipc4_mod_init_ext_obj_id { SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, }; -/* DP module memory configuration data object for ext_init object array */ +/* DP module memory configuration data object for object array */ struct sof_ipc4_mod_init_ext_dp_memory_data { + u32 domain_id; /* userspace domain ID */ + u32 stack_bytes; /* required stack size in bytes */ + u32 heap_bytes; /* required heap size in bytes */ +} __packed __aligned(4); + +/* + * This set of macros are very similar to the set above, but these are + * for building payload to SOF_IPC4_GLB_CREATE_PIPELINE message. + * + * Macros for creating struct sof_ipc4_glb_pipe_payload payload with + * its associated data. struct sof_ipc4_glb_pipe_payload should be the + * first piece of payload following SOF_IPC4_GLB_CREATE_PIPELINE msg, + * and its existence is indicated with SOF_IPC4_GLB_PIPE_PAYLOAD bit. + * + * The macros below apply to sof_ipc4_glb_pipe_payload.word0 + */ +#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT 0 +#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_MASK GENMASK(23, 0) +#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT 24 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK BIT(24) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT) + +struct sof_ipc4_glb_pipe_payload { + u32 word0; + u32 rsvd1; + u32 rsvd2; +} __packed __aligned(4); + +/* + * SOF_IPC4_GLB_CREATE_PIPELINE payload may be followed by arbitrary + * number of object array objects. SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY-bit + * indicates that an array object follows struct + * sof_ipc4_glb_pipe_payload. + * + * The object header's SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST-bit in struct + * sof_ipc4_glb_pipe_ext_object indicates if the array is continued + * with another object. The header has also fields to identify the + * object, SOF_IPC4_GLB_PIPE_EXT_OBJ_ID, and to indicate the object's + * size in 32-bit words, SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS, not + * including the header itself. + * + * The macros below apply to sof_ipc4_glb_pipe_ext_object.header + */ +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT 0 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK BIT(0) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT 1 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_MASK GENMASK(15, 1) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT 16 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_MASK GENMASK(31, 16) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT) + +struct sof_ipc4_glb_pipe_ext_object { + u32 header; + u32 data[]; +} __packed __aligned(4); + +enum sof_ipc4_glb_pipe_ext_obj_id { + SOF_IPC4_GLB_PIPE_DATA_ID_INVALID = 0, + SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, + SOF_IPC4_GLB_PIPE_DATA_ID_MAX = SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, +}; + +/* Pipeline memory configuration data object for ext_init object array */ +struct sof_ipc4_glb_pipe_ext_obj_memory_data { u32 domain_id; /* userspace domain ID */ - u32 stack_bytes; /* stack size in bytes, 0 means default size */ - u32 heap_bytes; /* stack size in bytes, 0 means default size */ + u32 stack_bytes; /* stack size in bytes */ + u32 heap_bytes; /* heap size in bytes */ } __packed __aligned(4); /** @}*/ diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h index dd6ee45ad096..e845687decf9 100644 --- a/include/sound/tas2781-dsp.h +++ b/include/sound/tas2781-dsp.h @@ -202,6 +202,11 @@ struct tasdevice_rca { struct tasdevice_config_info **cfg_info; int profile_cfg_id; /* + * Used among SmartAMP for PDM microphone recording or IV data + * capture. + */ + int capture_profile_id; + /* * Since version 0x105, the keyword 'init' was introduced into the * profile, which is used for chip initialization, particularly to * store common settings for other non-initialization profiles. @@ -222,7 +227,7 @@ void tasdevice_calbin_remove(void *context); int tasdevice_select_tuningprm_cfg(void *context, int prm, int cfg_no, int rca_conf_no); int tasdevice_prmg_load(void *context, int prm_no); -void tasdevice_tuning_switch(void *context, int state); +void tasdevice_tuning_switch(void *context, int state, bool is_cap); int tas2781_load_calibration(void *context, char *file_name, unsigned short i); diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h deleted file mode 100644 index b779d671a995..000000000000 --- a/include/sound/tlv320aic32x4.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data - * - * Copyright 2011 Vista Silicon S.L. - * - * Author: Javier Martin <javier.martin@vista-silicon.com> - */ - -#ifndef _AIC32X4_PDATA_H -#define _AIC32X4_PDATA_H - -#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 -#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 -#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 -#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 -#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 - -#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 -#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 - -/* GPIO API */ -#define AIC32X4_MFPX_DEFAULT_VALUE 0xff - -#define AIC32X4_MFP1_DIN_DISABLED 0 -#define AIC32X4_MFP1_DIN_ENABLED 0x2 -#define AIC32X4_MFP1_GPIO_IN 0x4 - -#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 -#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 - -#define AIC32X4_MFP_GPIO_ENABLED 0x4 - -#define AIC32X4_MFP5_GPIO_DISABLED 0x0 -#define AIC32X4_MFP5_GPIO_INPUT 0x8 -#define AIC32X4_MFP5_GPIO_OUTPUT 0xc -#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 -#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 - -struct aic32x4_setup_data { - unsigned int gpio_func[5]; -}; -#endif diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h index 8b2c16b524f7..151a05b13dfc 100644 --- a/include/sound/wm8904.h +++ b/include/sound/wm8904.h @@ -116,7 +116,7 @@ #define WM8904_DRC_REGS 4 #define WM8904_EQ_REGS 24 -/** +/* * DRC configurations are specified with a label and a set of register * values to write (the enable bits will be ignored). At runtime an * enumerated control will be presented for each DRC block allowing @@ -131,7 +131,7 @@ struct wm8904_drc_cfg { u16 regs[WM8904_DRC_REGS]; }; -/** +/* * ReTune Mobile configurations are specified with a label, sample * rate and set of values to write (the enable bits will be ignored). * diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h deleted file mode 100644 index 940c4269322b..000000000000 --- a/include/uapi/sound/skl-tplg-interface.h +++ /dev/null @@ -1,168 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * skl-tplg-interface.h - Intel DSP FW private data interface - * - * Copyright (C) 2015 Intel Corp - * Author: Jeeja KP <jeeja.kp@intel.com> - * Nilofer, Samreen <samreen.nilofer@intel.com> - */ - -#ifndef __HDA_TPLG_INTERFACE_H__ -#define __HDA_TPLG_INTERFACE_H__ - -#include <linux/types.h> - -/* - * Default types range from 0~12. type can range from 0 to 0xff - * SST types start at higher to avoid any overlapping in future - */ -#define SKL_CONTROL_TYPE_BYTE_TLV 0x100 -#define SKL_CONTROL_TYPE_MIC_SELECT 0x102 -#define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 -#define SKL_CONTROL_TYPE_MULTI_IO_SELECT_DMIC 0x104 - -#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ -#define MAX_IN_QUEUE 8 -#define MAX_OUT_QUEUE 8 - -#define SKL_UUID_STR_SZ 40 -/* Event types goes here */ -/* Reserve event type 0 for no event handlers */ -enum skl_event_types { - SKL_EVENT_NONE = 0, - SKL_MIXER_EVENT, - SKL_MUX_EVENT, - SKL_VMIXER_EVENT, - SKL_PGA_EVENT -}; - -/** - * enum skl_ch_cfg - channel configuration - * - * @SKL_CH_CFG_MONO: One channel only - * @SKL_CH_CFG_STEREO: L & R - * @SKL_CH_CFG_2_1: L, R & LFE - * @SKL_CH_CFG_3_0: L, C & R - * @SKL_CH_CFG_3_1: L, C, R & LFE - * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs - * @SKL_CH_CFG_4_0: L, C, R & Cs - * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs - * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE - * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two - * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ] - * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ] - * @SKL_CH_CFG_INVALID: Invalid - */ -enum skl_ch_cfg { - SKL_CH_CFG_MONO = 0, - SKL_CH_CFG_STEREO = 1, - SKL_CH_CFG_2_1 = 2, - SKL_CH_CFG_3_0 = 3, - SKL_CH_CFG_3_1 = 4, - SKL_CH_CFG_QUATRO = 5, - SKL_CH_CFG_4_0 = 6, - SKL_CH_CFG_5_0 = 7, - SKL_CH_CFG_5_1 = 8, - SKL_CH_CFG_DUAL_MONO = 9, - SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, - SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, - SKL_CH_CFG_7_1 = 12, - SKL_CH_CFG_4_CHANNEL = SKL_CH_CFG_7_1, - SKL_CH_CFG_INVALID -}; - -enum skl_module_type { - SKL_MODULE_TYPE_MIXER = 0, - SKL_MODULE_TYPE_COPIER, - SKL_MODULE_TYPE_UPDWMIX, - SKL_MODULE_TYPE_SRCINT, - SKL_MODULE_TYPE_ALGO, - SKL_MODULE_TYPE_BASE_OUTFMT, - SKL_MODULE_TYPE_KPB, - SKL_MODULE_TYPE_MIC_SELECT, -}; - -enum skl_core_affinity { - SKL_AFFINITY_CORE_0 = 0, - SKL_AFFINITY_CORE_1, - SKL_AFFINITY_CORE_MAX -}; - -enum skl_pipe_conn_type { - SKL_PIPE_CONN_TYPE_NONE = 0, - SKL_PIPE_CONN_TYPE_FE, - SKL_PIPE_CONN_TYPE_BE -}; - -enum skl_hw_conn_type { - SKL_CONN_NONE = 0, - SKL_CONN_SOURCE = 1, - SKL_CONN_SINK = 2 -}; - -enum skl_dev_type { - SKL_DEVICE_BT = 0x0, - SKL_DEVICE_DMIC = 0x1, - SKL_DEVICE_I2S = 0x2, - SKL_DEVICE_SLIMBUS = 0x3, - SKL_DEVICE_HDALINK = 0x4, - SKL_DEVICE_HDAHOST = 0x5, - SKL_DEVICE_NONE -}; - -/** - * enum skl_interleaving - interleaving style - * - * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] - * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] - */ -enum skl_interleaving { - SKL_INTERLEAVING_PER_CHANNEL = 0, - SKL_INTERLEAVING_PER_SAMPLE = 1, -}; - -enum skl_sample_type { - SKL_SAMPLE_TYPE_INT_MSB = 0, - SKL_SAMPLE_TYPE_INT_LSB = 1, - SKL_SAMPLE_TYPE_INT_SIGNED = 2, - SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, - SKL_SAMPLE_TYPE_FLOAT = 4 -}; - -enum module_pin_type { - /* All pins of the module takes same PCM inputs or outputs - * e.g. mixout - */ - SKL_PIN_TYPE_HOMOGENEOUS, - /* All pins of the module takes different PCM inputs or outputs - * e.g mux - */ - SKL_PIN_TYPE_HETEROGENEOUS, -}; - -enum skl_module_param_type { - SKL_PARAM_DEFAULT = 0, - SKL_PARAM_INIT, - SKL_PARAM_SET, - SKL_PARAM_BIND -}; - -struct skl_dfw_algo_data { - __u32 set_params:2; - __u32 rsvd:30; - __u32 param_id; - __u32 max; - char params[]; -} __packed; - -enum skl_tkn_dir { - SKL_DIR_IN, - SKL_DIR_OUT -}; - -enum skl_tuple_type { - SKL_TYPE_TUPLE, - SKL_TYPE_DATA -}; - -#endif diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h index 6b8102eaa121..cf8ab6b85e34 100644 --- a/include/uapi/sound/snd_ar_tokens.h +++ b/include/uapi/sound/snd_ar_tokens.h @@ -58,7 +58,7 @@ enum ar_event_types { #define SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX 256 #define SND_SOC_AR_TPLG_VOL_CTL 257 -/** +/* * %AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: Sub Graph Instance Id * * %AR_TKN_U32_SUB_GRAPH_PERF_MODE: Performance mode of subgraph @@ -168,6 +168,60 @@ enum ar_event_types { * LOG_WAIT = 0, * LOG_IMMEDIATELY = 1 * + * %AR_TKN_U16_MODULE_SYNC_SRC: Frame sync source + * AR_AUDIO_IF_SYNC_SRC_EXTERNAL = 0, + * AR_AUDIO_IF_SYNC_SRC_INTERNAL = 1 + * + * %AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: Enable data-out tri-state control + * AR_AUDIO_IF_CTRL_DATA_OE_DISABLE = 0, + * AR_AUDIO_IF_CTRL_DATA_OE_ENABLE = 1 + * + * %AR_TKN_U32_MODULE_SLOT_MASK: Active TDM slot bitmask + * + * %AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: Number of slots per TDM frame + * + * %AR_TKN_U16_MODULE_SLOT_WIDTH: Slot width in bits (16 or 32) + * + * %AR_TKN_U16_MODULE_SYNC_MODE: Frame sync mode + * AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT = 0, + * AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT = 1, + * AR_AUDIO_IF_FRAME_SYNC_MODE_LONG = 2 + * + * %AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: Invert frame sync pulse polarity + * AR_AUDIO_IF_SYNC_NORMAL = 0, + * AR_AUDIO_IF_SYNC_INVERTED = 1 + * + * %AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: Data delay relative to frame sync + * AR_AUDIO_IF_DATA_DELAY_NONE = 0, + * AR_AUDIO_IF_DATA_DELAY_1_CYCLE = 1, + * AR_AUDIO_IF_DATA_DELAY_2_CYCLE = 2 + * + * %AR_TKN_U16_MODULE_INTF_MODE: Audio IF interface mode + * AR_AUDIO_IF_INTF_MODE_TDM = 0, + * AR_AUDIO_IF_INTF_MODE_PCM = 1, + * AR_AUDIO_IF_INTF_MODE_I2S = 2 + * + * %AR_TKN_U16_MODULE_QAIF_TYPE: QAIF hardware port type index + * AR_AUDIO_IF_QAIF = 0, + * AR_AUDIO_IF_QAIF_VA = 1 + * + * %AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: Active lane bitmask for multi-lane + * + * %AR_TKN_U32_MODULE_FRAME_SYNC_RATE: Frame sync rate in Hz + * + * %AR_TKN_U16_MODULE_BIT_CLK_TYPE: Bit clock type + * AR_AUDIO_IF_BIT_CLK_INTERNAL = 0, + * AR_AUDIO_IF_BIT_CLK_EXTERNAL = 1, + * AR_AUDIO_IF_BIT_CLK_SKIP = 2 + * + * %AR_TKN_U8_MODULE_INV_INT_BIT_CLK: Invert internal bit clock + * AR_AUDIO_IF_CLK_NORMAL = 0, + * AR_AUDIO_IF_CLK_INVERTED = 1 + * + * %AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: Invert external bit clock + * AR_AUDIO_IF_CLK_NORMAL = 0, + * AR_AUDIO_IF_CLK_INVERTED = 1 + * * %AR_TKN_DAI_INDEX: dai index * */ @@ -240,6 +294,45 @@ enum ar_event_types { #define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260 #define AR_TKN_U32_MODULE_LOG_MODE 261 +#define AR_TKN_U16_MODULE_SYNC_SRC 262 +#define AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE 263 +#define AR_TKN_U32_MODULE_SLOT_MASK 264 +#define AR_TKN_U16_MODULE_NSLOTS_PER_FRAME 265 +#define AR_TKN_U16_MODULE_SLOT_WIDTH 266 +#define AR_TKN_U16_MODULE_SYNC_MODE 267 +#define AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE 268 +#define AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY 269 +#define AR_TKN_U16_MODULE_INTF_MODE 270 +#define AR_TKN_U16_MODULE_QAIF_TYPE 271 +#define AR_TKN_U32_MODULE_ACTIVE_LANE_MASK 272 +#define AR_TKN_U32_MODULE_FRAME_SYNC_RATE 273 +#define AR_TKN_U16_MODULE_BIT_CLK_TYPE 274 +#define AR_TKN_U8_MODULE_INV_INT_BIT_CLK 275 +#define AR_TKN_U8_MODULE_INV_EXT_BIT_CLK 276 + +#define AR_AUDIO_IF_SYNC_SRC_EXTERNAL 0 +#define AR_AUDIO_IF_SYNC_SRC_INTERNAL 1 +#define AR_AUDIO_IF_CTRL_DATA_OE_DISABLE 0 +#define AR_AUDIO_IF_CTRL_DATA_OE_ENABLE 1 +#define AR_AUDIO_IF_INTF_MODE_TDM 0 +#define AR_AUDIO_IF_INTF_MODE_PCM 1 +#define AR_AUDIO_IF_INTF_MODE_I2S 2 +#define AR_AUDIO_IF_QAIF 0 +#define AR_AUDIO_IF_QAIF_VA 1 +#define AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT 0 +#define AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT 1 +#define AR_AUDIO_IF_FRAME_SYNC_MODE_LONG 2 +#define AR_AUDIO_IF_SYNC_NORMAL 0 +#define AR_AUDIO_IF_SYNC_INVERTED 1 +#define AR_AUDIO_IF_DATA_DELAY_NONE 0 +#define AR_AUDIO_IF_DATA_DELAY_1_CYCLE 1 +#define AR_AUDIO_IF_DATA_DELAY_2_CYCLE 2 +#define AR_AUDIO_IF_BIT_CLK_INTERNAL 0 +#define AR_AUDIO_IF_BIT_CLK_EXTERNAL 1 +#define AR_AUDIO_IF_BIT_CLK_SKIP 2 +#define AR_AUDIO_IF_CLK_NORMAL 0 +#define AR_AUDIO_IF_CLK_INVERTED 1 + #define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006 struct audioreach_module_priv_data { __le32 size; /* size in bytes of the array, including all elements */ diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h deleted file mode 100644 index defeb0c6ed20..000000000000 --- a/include/uapi/sound/snd_sst_tokens.h +++ /dev/null @@ -1,324 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * snd_sst_tokens.h - Intel SST tokens definition - * - * Copyright (C) 2016 Intel Corp - * Author: Shreyas NC <shreyas.nc@intel.com> - */ -#ifndef __SND_SST_TOKENS_H__ -#define __SND_SST_TOKENS_H__ - -/** - * %SKL_TKN_UUID: Module UUID - * - * %SKL_TKN_U8_BLOCK_TYPE: Type of the private data block.Can be: - * tuples, bytes, short and words - * - * %SKL_TKN_U8_IN_PIN_TYPE: Input pin type, - * homogenous=0, heterogenous=1 - * - * %SKL_TKN_U8_OUT_PIN_TYPE: Output pin type, - * homogenous=0, heterogenous=1 - * %SKL_TKN_U8_DYN_IN_PIN: Configure Input pin dynamically - * if true - * - * %SKL_TKN_U8_DYN_OUT_PIN: Configure Output pin dynamically - * if true - * - * %SKL_TKN_U8_IN_QUEUE_COUNT: Store the number of Input pins - * - * %SKL_TKN_U8_OUT_QUEUE_COUNT: Store the number of Output pins - * - * %SKL_TKN_U8_TIME_SLOT: TDM slot number - * - * %SKL_TKN_U8_CORE_ID: Stores module affinity value.Can take - * the values: - * SKL_AFFINITY_CORE_0 = 0, - * SKL_AFFINITY_CORE_1, - * SKL_AFFINITY_CORE_MAX - * - * %SKL_TKN_U8_MOD_TYPE: Module type value. - * - * %SKL_TKN_U8_CONN_TYPE: Module connection type can be a FE, - * BE or NONE as defined : - * SKL_PIPE_CONN_TYPE_NONE = 0, - * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA) - * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA) - * - * %SKL_TKN_U8_DEV_TYPE: Type of device to which the module is - * connected - * Can take the values: - * SKL_DEVICE_BT = 0x0, - * SKL_DEVICE_DMIC = 0x1, - * SKL_DEVICE_I2S = 0x2, - * SKL_DEVICE_SLIMBUS = 0x3, - * SKL_DEVICE_HDALINK = 0x4, - * SKL_DEVICE_HDAHOST = 0x5, - * SKL_DEVICE_NONE - * - * %SKL_TKN_U8_HW_CONN_TYPE: Connection type of the HW to which the - * module is connected - * SKL_CONN_NONE = 0, - * SKL_CONN_SOURCE = 1, - * SKL_CONN_SINK = 2 - * - * %SKL_TKN_U16_PIN_INST_ID: Stores the pin instance id - * - * %SKL_TKN_U16_MOD_INST_ID: Stores the mdule instance id - * - * %SKL_TKN_U32_MAX_MCPS: Module max mcps value - * - * %SKL_TKN_U32_MEM_PAGES: Module resource pages - * - * %SKL_TKN_U32_OBS: Stores Output Buffer size - * - * %SKL_TKN_U32_IBS: Stores input buffer size - * - * %SKL_TKN_U32_VBUS_ID: Module VBUS_ID. PDM=0, SSP0=0, - * SSP1=1,SSP2=2, - * SSP3=3, SSP4=4, - * SSP5=5, SSP6=6,INVALID - * - * %SKL_TKN_U32_PARAMS_FIXUP: Module Params fixup mask - * %SKL_TKN_U32_CONVERTER: Module params converter mask - * %SKL_TKN_U32_PIPE_ID: Stores the pipe id - * - * %SKL_TKN_U32_PIPE_CONN_TYPE: Type of the token to which the pipe is - * connected to. It can be - * SKL_PIPE_CONN_TYPE_NONE = 0, - * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA), - * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA), - * - * %SKL_TKN_U32_PIPE_PRIORITY: Pipe priority value - * %SKL_TKN_U32_PIPE_MEM_PGS: Pipe resource pages - * - * %SKL_TKN_U32_DIR_PIN_COUNT: Value for the direction to set input/output - * formats and the pin count. - * The first 4 bits have the direction - * value and the next 4 have - * the pin count value. - * SKL_DIR_IN = 0, SKL_DIR_OUT = 1. - * The input and output formats - * share the same set of tokens - * with the distinction between input - * and output made by reading direction - * token. - * - * %SKL_TKN_U32_FMT_CH: Supported channel count - * - * %SKL_TKN_U32_FMT_FREQ: Supported frequency/sample rate - * - * %SKL_TKN_U32_FMT_BIT_DEPTH: Supported container size - * - * %SKL_TKN_U32_FMT_SAMPLE_SIZE:Number of samples in the container - * - * %SKL_TKN_U32_FMT_CH_CONFIG: Supported channel configurations for the - * input/output. - * - * %SKL_TKN_U32_FMT_INTERLEAVE: Interleaving style which can be per - * channel or per sample. The values can be : - * SKL_INTERLEAVING_PER_CHANNEL = 0, - * SKL_INTERLEAVING_PER_SAMPLE = 1, - * - * %SKL_TKN_U32_FMT_SAMPLE_TYPE: - * Specifies the sample type. Can take the - * values: SKL_SAMPLE_TYPE_INT_MSB = 0, - * SKL_SAMPLE_TYPE_INT_LSB = 1, - * SKL_SAMPLE_TYPE_INT_SIGNED = 2, - * SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, - * SKL_SAMPLE_TYPE_FLOAT = 4 - * - * %SKL_TKN_U32_CH_MAP: Channel map values - * %SKL_TKN_U32_MOD_SET_PARAMS: It can take these values: - * SKL_PARAM_DEFAULT, SKL_PARAM_INIT, - * SKL_PARAM_SET, SKL_PARAM_BIND - * - * %SKL_TKN_U32_MOD_PARAM_ID: ID of the module params - * - * %SKL_TKN_U32_CAPS_SET_PARAMS: - * Set params value - * - * %SKL_TKN_U32_CAPS_PARAMS_ID: Params ID - * - * %SKL_TKN_U32_CAPS_SIZE: Caps size - * - * %SKL_TKN_U32_PROC_DOMAIN: Specify processing domain - * - * %SKL_TKN_U32_LIB_COUNT: Specifies the number of libraries - * - * %SKL_TKN_STR_LIB_NAME: Specifies the library name - * - * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe - * - * %SKL_TKL_U32_D0I3_CAPS: Specifies the D0i3 capability for module - * - * %SKL_TKN_U32_DMA_BUF_SIZE: DMA buffer size in millisec - * - * %SKL_TKN_U32_PIPE_DIR: Specifies pipe direction. Can be - * playback/capture. - * - * %SKL_TKN_U32_NUM_CONFIGS: Number of pipe configs - * - * %SKL_TKN_U32_PATH_MEM_PGS: Size of memory (in pages) required for pipeline - * and its data - * - * %SKL_TKN_U32_PIPE_CONFIG_ID: Config id for the modules in the pipe - * and PCM params supported by that pipe - * config. This is used as index to fill - * up the pipe config and module config - * structure. - * - * %SKL_TKN_U32_CFG_FREQ: - * %SKL_TKN_U8_CFG_CHAN: - * %SKL_TKN_U8_CFG_BPS: PCM params (freq, channels, bits per sample) - * supported for each of the pipe configs. - * - * %SKL_TKN_CFG_MOD_RES_ID: Module's resource index for each of the - * pipe config - * - * %SKL_TKN_CFG_MOD_FMT_ID: Module's interface index for each of the - * pipe config - * - * %SKL_TKN_U8_NUM_MOD: Number of modules in the manifest - * - * %SKL_TKN_MM_U8_MOD_IDX: Current index of the module in the manifest - * - * %SKL_TKN_MM_U8_NUM_RES: Number of resources for the module - * - * %SKL_TKN_MM_U8_NUM_INTF: Number of interfaces for the module - * - * %SKL_TKN_MM_U32_RES_ID: Resource index for the resource info to - * be filled into. - * A module can support multiple resource - * configuration and is represnted as a - * resource table. This index is used to - * fill information into appropriate index. - * - * %SKL_TKN_MM_U32_CPS: DSP cycles per second - * - * %SKL_TKN_MM_U32_DMA_SIZE: Allocated buffer size for gateway DMA - * - * %SKL_TKN_MM_U32_CPC: DSP cycles allocated per frame - * - * %SKL_TKN_MM_U32_RES_PIN_ID: Resource pin index in the module - * - * %SKL_TKN_MM_U32_INTF_PIN_ID: Interface index in the module - * - * %SKL_TKN_MM_U32_PIN_BUF: Buffer size of the module pin - * - * %SKL_TKN_MM_U32_FMT_ID: Format index for each of the interface/ - * format information to be filled into. - * - * %SKL_TKN_MM_U32_NUM_IN_FMT: Number of input formats - * %SKL_TKN_MM_U32_NUM_OUT_FMT: Number of output formats - * - * %SKL_TKN_U32_ASTATE_IDX: Table Index for the A-State entry to be filled - * with kcps and clock source - * - * %SKL_TKN_U32_ASTATE_COUNT: Number of valid entries in A-State table - * - * %SKL_TKN_U32_ASTATE_KCPS: Specifies the core load threshold (in kilo - * cycles per second) below which DSP is clocked - * from source specified by clock source. - * - * %SKL_TKN_U32_ASTATE_CLK_SRC: Clock source for A-State entry - * - * %SKL_TKN_U32_FMT_CFG_IDX: Format config index - * - * module_id and loadable flags dont have tokens as these values will be - * read from the DSP FW manifest - * - * Tokens defined can be used either in the manifest or widget private data. - * - * SKL_TKN_MM is used as a suffix for all tokens that represent - * module data in the manifest. - */ -enum SKL_TKNS { - SKL_TKN_UUID = 1, - SKL_TKN_U8_NUM_BLOCKS, - SKL_TKN_U8_BLOCK_TYPE, - SKL_TKN_U8_IN_PIN_TYPE, - SKL_TKN_U8_OUT_PIN_TYPE, - SKL_TKN_U8_DYN_IN_PIN, - SKL_TKN_U8_DYN_OUT_PIN, - SKL_TKN_U8_IN_QUEUE_COUNT, - SKL_TKN_U8_OUT_QUEUE_COUNT, - SKL_TKN_U8_TIME_SLOT, - SKL_TKN_U8_CORE_ID, - SKL_TKN_U8_MOD_TYPE, - SKL_TKN_U8_CONN_TYPE, - SKL_TKN_U8_DEV_TYPE, - SKL_TKN_U8_HW_CONN_TYPE, - SKL_TKN_U16_MOD_INST_ID, - SKL_TKN_U16_BLOCK_SIZE, - SKL_TKN_U32_MAX_MCPS, - SKL_TKN_U32_MEM_PAGES, - SKL_TKN_U32_OBS, - SKL_TKN_U32_IBS, - SKL_TKN_U32_VBUS_ID, - SKL_TKN_U32_PARAMS_FIXUP, - SKL_TKN_U32_CONVERTER, - SKL_TKN_U32_PIPE_ID, - SKL_TKN_U32_PIPE_CONN_TYPE, - SKL_TKN_U32_PIPE_PRIORITY, - SKL_TKN_U32_PIPE_MEM_PGS, - SKL_TKN_U32_DIR_PIN_COUNT, - SKL_TKN_U32_FMT_CH, - SKL_TKN_U32_FMT_FREQ, - SKL_TKN_U32_FMT_BIT_DEPTH, - SKL_TKN_U32_FMT_SAMPLE_SIZE, - SKL_TKN_U32_FMT_CH_CONFIG, - SKL_TKN_U32_FMT_INTERLEAVE, - SKL_TKN_U32_FMT_SAMPLE_TYPE, - SKL_TKN_U32_FMT_CH_MAP, - SKL_TKN_U32_PIN_MOD_ID, - SKL_TKN_U32_PIN_INST_ID, - SKL_TKN_U32_MOD_SET_PARAMS, - SKL_TKN_U32_MOD_PARAM_ID, - SKL_TKN_U32_CAPS_SET_PARAMS, - SKL_TKN_U32_CAPS_PARAMS_ID, - SKL_TKN_U32_CAPS_SIZE, - SKL_TKN_U32_PROC_DOMAIN, - SKL_TKN_U32_LIB_COUNT, - SKL_TKN_STR_LIB_NAME, - SKL_TKN_U32_PMODE, - SKL_TKL_U32_D0I3_CAPS, /* Typo added at v4.10 */ - SKL_TKN_U32_D0I3_CAPS = SKL_TKL_U32_D0I3_CAPS, - SKL_TKN_U32_DMA_BUF_SIZE, - - SKL_TKN_U32_PIPE_DIRECTION, - SKL_TKN_U32_PIPE_CONFIG_ID, - SKL_TKN_U32_NUM_CONFIGS, - SKL_TKN_U32_PATH_MEM_PGS, - - SKL_TKN_U32_CFG_FREQ, - SKL_TKN_U8_CFG_CHAN, - SKL_TKN_U8_CFG_BPS, - SKL_TKN_CFG_MOD_RES_ID, - SKL_TKN_CFG_MOD_FMT_ID, - SKL_TKN_U8_NUM_MOD, - - SKL_TKN_MM_U8_MOD_IDX, - SKL_TKN_MM_U8_NUM_RES, - SKL_TKN_MM_U8_NUM_INTF, - SKL_TKN_MM_U32_RES_ID, - SKL_TKN_MM_U32_CPS, - SKL_TKN_MM_U32_DMA_SIZE, - SKL_TKN_MM_U32_CPC, - SKL_TKN_MM_U32_RES_PIN_ID, - SKL_TKN_MM_U32_INTF_PIN_ID, - SKL_TKN_MM_U32_PIN_BUF, - SKL_TKN_MM_U32_FMT_ID, - SKL_TKN_MM_U32_NUM_IN_FMT, - SKL_TKN_MM_U32_NUM_OUT_FMT, - - SKL_TKN_U32_ASTATE_IDX, - SKL_TKN_U32_ASTATE_COUNT, - SKL_TKN_U32_ASTATE_KCPS, - SKL_TKN_U32_ASTATE_CLK_SRC, - - SKL_TKN_U32_FMT_CFG_IDX = 96, - SKL_TKN_MAX = SKL_TKN_U32_FMT_CFG_IDX, -}; - -#endif diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index f4a7baadb44d..d42adbef0478 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -111,8 +111,8 @@ #define SOF_TKN_COMP_SCHED_DOMAIN 418 #define SOF_TKN_COMP_DOMAIN_ID 419 -#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 420 -#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 421 +#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 420 +#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 421 /* SSP */ #define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 @@ -223,6 +223,7 @@ #define SOF_TKN_AMD_ACPI2S_RATE 1700 #define SOF_TKN_AMD_ACPI2S_CH 1701 #define SOF_TKN_AMD_ACPI2S_TDM_MODE 1702 +#define SOF_TKN_AMD_ACPI2S_FORMAT 1703 /* MICFIL PDM */ #define SOF_TKN_IMX_MICFIL_RATE 2000 diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 6c502c34e015..5e5f46c9fb6b 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -171,13 +171,13 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) case HDA_GEN_PCM_ACT_OPEN: pm_runtime_get_sync(dev); scoped_guard(mutex, &tas_hda->priv->codec_lock) { - tasdevice_tuning_switch(tas_hda->priv, 0); + tasdevice_tuning_switch(tas_hda->priv, 0, false); tas_hda->priv->playback_started = true; } break; case HDA_GEN_PCM_ACT_CLOSE: scoped_guard(mutex, &tas_hda->priv->codec_lock) { - tasdevice_tuning_switch(tas_hda->priv, 1); + tasdevice_tuning_switch(tas_hda->priv, 1, false); tas_hda->priv->playback_started = false; } @@ -720,7 +720,7 @@ static int tas2781_runtime_suspend(struct device *dev) * Stop the playback if it's unused. */ if (tas_hda->priv->playback_started) { - tasdevice_tuning_switch(tas_hda->priv, 1); + tasdevice_tuning_switch(tas_hda->priv, 1, false); tas_hda->priv->playback_started = false; } @@ -750,7 +750,7 @@ static int tas2781_system_suspend(struct device *dev) /* Shutdown chip before system suspend */ if (tas_hda->priv->playback_started) - tasdevice_tuning_switch(tas_hda->priv, 1); + tasdevice_tuning_switch(tas_hda->priv, 1, false); /* * Reset GPIO may be shared, so cannot reset here. @@ -783,7 +783,7 @@ static int tas2781_system_resume(struct device *dev) TASDEVICE_BIN_BLK_PRE_POWER_UP); if (tas_hda->priv->playback_started) - tasdevice_tuning_switch(tas_hda->priv, 0); + tasdevice_tuning_switch(tas_hda->priv, 0, false); return 0; } diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index d3b2a746e1a7..da71b57a26ee 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -399,11 +399,11 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) pm_runtime_get_sync(dev); guard(mutex)(&tas_priv->codec_lock); if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) - tasdevice_tuning_switch(tas_hda->priv, 0); + tasdevice_tuning_switch(tas_hda->priv, 0, false); } else if (action == HDA_GEN_PCM_ACT_CLOSE) { guard(mutex)(&tas_priv->codec_lock); if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) - tasdevice_tuning_switch(tas_priv, 1); + tasdevice_tuning_switch(tas_priv, 1, false); pm_runtime_put_autosuspend(dev); } } @@ -847,7 +847,7 @@ static int tas2781_runtime_suspend(struct device *dev) if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK && tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 1); + tasdevice_tuning_switch(tas_priv, 1, false); tas_priv->tasdevice[tas_priv->index].cur_book = -1; tas_priv->tasdevice[tas_priv->index].cur_conf = -1; @@ -864,7 +864,7 @@ static int tas2781_runtime_resume(struct device *dev) if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK && tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 0); + tasdevice_tuning_switch(tas_priv, 0, false); return 0; } @@ -882,7 +882,7 @@ static int tas2781_system_suspend(struct device *dev) /* Shutdown chip before system suspend */ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK && tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 1); + tasdevice_tuning_switch(tas_priv, 1, false); return 0; } @@ -917,7 +917,7 @@ static int tas2781_system_resume(struct device *dev) tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; if (tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 0); + tasdevice_tuning_switch(tas_priv, 0, false); } return ret; diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 88e4230d66c3..3021dc498c85 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -63,6 +63,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), }, }, + { + /* HP OmniBook X Flip 14-kc0xxx (Strix Point, ACP 7.2) */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8EA1"), + }, + }, {} }; @@ -379,5 +386,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = { }; EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines); +struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[] = { + { + .id = "AMDI1010", + .drv_name = "acp7x-dsp", + .pdata = &acp_quirk_data, + .fw_filename = "sof-acp7x.ri", + .sof_tplg_filename = "sof-acp7x.tplg", + }, + {}, +}; +EXPORT_SYMBOL(snd_soc_acpi_amd_acp7x_sof_machines); + MODULE_DESCRIPTION("AMD ACP Machine Configuration Module"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 6ad70aa0ea83..ee75737202dc 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -691,12 +691,10 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) { u16 dscr_idx; u32 intr_flag, ext_intr_status; - struct audio_drv_data *irq_data; + struct audio_drv_data *irq_data = arg; void __iomem *acp_mmio; - struct device *dev = arg; bool valid_irq = false; - irq_data = dev_get_drvdata(dev); acp_mmio = irq_data->acp_mmio; ext_intr_status = acp_reg_read(acp_mmio, mmACP_EXTERNAL_INTR_STAT); @@ -1291,10 +1289,10 @@ static int acp_audio_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return -ENODEV; + return irq; status = devm_request_irq(&pdev->dev, irq, dma_irq_handler, - 0, "ACP_IRQ", &pdev->dev); + 0, "ACP_IRQ", audio_drv_data); if (status) { dev_err(&pdev->dev, "ACP IRQ request failed\n"); return status; diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 283a674c7e2c..bb58a9d34993 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -686,6 +686,10 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d return 0; } +static const u64 acp_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A; + const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { .startup = acp_i2s_startup, .hw_params = acp_i2s_hwparams, @@ -693,6 +697,8 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { .trigger = acp_i2s_trigger, .set_fmt = acp_i2s_set_fmt, .set_tdm_slot = acp_i2s_set_tdm_slot, + .auto_selectable_formats = &acp_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, "SND_SOC_ACP_COMMON"); diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index ef784cca13f2..01a0aaa60246 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -938,15 +938,10 @@ static int acp_max98388_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, MAX98388_CODEC_DAI); - int ret; - ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF); - if (ret < 0) - return ret; - - return ret; + return snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF); } static const struct snd_soc_ops acp_max98388_ops = { diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index f83708755ed1..98771323eace 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -118,17 +118,10 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id if (!chip) return -ENOMEM; - if (pci_enable_device(pci)) + if (pcim_enable_device(pci)) return dev_err_probe(&pci->dev, -ENODEV, "pci_enable_device failed\n"); - ret = pci_request_regions(pci, "AMD ACP3x audio"); - if (ret < 0) { - dev_err(&pci->dev, "pci_request_regions failed\n"); - ret = -ENOMEM; - goto disable_pci; - } - pci_set_master(pci); chip->acp_rev = pci->revision; @@ -161,24 +154,21 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id break; default: dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); - ret = -EINVAL; - goto release_regions; + return -EINVAL; } chip->flag = flag; addr = pci_resource_start(pci, 0); - chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0)); - if (!chip->base) { - ret = -ENOMEM; - goto release_regions; - } + chip->base = pcim_iomap_region(pci, 0, "AMD ACP3x audio"); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); chip->addr = addr; chip->acp_hw_ops_init(chip); ret = acp_hw_init(chip); if (ret) - goto release_regions; + goto de_init; ret = devm_request_irq(dev, pci->irq, irq_handler, IRQF_SHARED, "ACP_I2S_IRQ", chip); @@ -214,10 +204,6 @@ skip_pdev_creation: de_init: acp_hw_deinit(chip); -release_regions: - pci_release_regions(pci); -disable_pci: - pci_disable_device(pci); return ret; }; diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index e8b6819cc4b4..6eac42bac855 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -432,7 +432,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) if (!soc_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs); if (ret < 0) return ret; @@ -521,15 +521,15 @@ static int mc_probe(struct platform_device *pdev) dmi_check_system(soc_sdw_quirk_table); if (quirk_override != -1) { - dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", soc_sdw_quirk, quirk_override); soc_sdw_quirk = quirk_override; } - log_quirks(card->dev); + log_quirks(&pdev->dev); ctx->mc_quirk = soc_sdw_quirk; - dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); + dev_dbg(&pdev->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ for (i = 0; i < ctx->codec_info_list_count; i++) codec_info_list[i].amp_num = 0; @@ -546,12 +546,12 @@ static int mc_probe(struct platform_device *pdev) for (i = 0; i < ctx->codec_info_list_count; i++) amp_num += codec_info_list[i].amp_num; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, " cfg-amp:%d", amp_num); if (!card->components) return -ENOMEM; if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) { - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s mic:acp-dmic cfg-mics:%d", card->components, 1); @@ -560,9 +560,9 @@ static int mc_probe(struct platform_device *pdev) } /* Register the card */ - ret = devm_snd_soc_register_card(card->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { - dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); asoc_sdw_mc_dailink_exit_loop(card); return ret; } @@ -580,8 +580,8 @@ static void mc_remove(struct platform_device *pdev) } static const struct platform_device_id mc_id_table[] = { - { "amd_sdw", }, - {} + { .name = "amd_sdw" }, + { } }; MODULE_DEVICE_TABLE(platform, mc_id_table); diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a423853f3a97..a9cd1f335167 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -303,7 +303,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) if (!sof_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_devs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); if (ret < 0) return ret; @@ -388,12 +388,12 @@ static int mc_probe(struct platform_device *pdev) dmi_check_system(sof_sdw_quirk_table); if (quirk_override != -1) { - dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", sof_sdw_quirk, quirk_override); sof_sdw_quirk = quirk_override; } - log_quirks(card->dev); + log_quirks(&pdev->dev); ctx->mc_quirk = sof_sdw_quirk; /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ @@ -412,15 +412,15 @@ static int mc_probe(struct platform_device *pdev) for (i = 0; i < ctx->codec_info_list_count; i++) amp_num += codec_info_list[i].amp_num; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, " cfg-amp:%d", amp_num); if (!card->components) return -ENOMEM; /* Register the card */ - ret = devm_snd_soc_register_card(card->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { - dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); asoc_sdw_mc_dailink_exit_loop(card); return ret; } @@ -438,8 +438,8 @@ static void mc_remove(struct platform_device *pdev) } static const struct platform_device_id mc_id_table[] = { - { "amd_sof_sdw", }, - {} + { .name = "amd_sof_sdw" }, + { } }; MODULE_DEVICE_TABLE(platform, mc_id_table); diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 36ecef7013b9..8874151e159a 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -128,31 +128,31 @@ static int acp_sof_probe(struct platform_device *pdev) static const struct platform_device_id board_ids[] = { { .name = "rt5682-rt1019", - .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data + .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data, }, { .name = "rt5682-max", - .driver_data = (kernel_ulong_t)&sof_rt5682_max_data + .driver_data = (kernel_ulong_t)&sof_rt5682_max_data, }, { .name = "rt5682s-max", - .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data + .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data, }, { .name = "rt5682s-rt1019", - .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data + .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data, }, { .name = "nau8825-max", - .driver_data = (kernel_ulong_t)&sof_nau8825_data + .driver_data = (kernel_ulong_t)&sof_nau8825_data, }, { .name = "rt5682s-hs-rt1019", - .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data + .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data, }, { .name = "nau8821-max", - .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data + .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data, }, { } }; diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index ccd01152c87d..815e088b437d 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -659,6 +659,56 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_l1u0_tas2783x2_l1u8b[] = { {} }; +static const struct snd_soc_acpi_adr_device rt712_vb_l1u0_adr[] = { + { + .adr = 0x000130025D071201ull, + /* + * On this platform speakers are provided by two TAS2783 amps, + * so the AIF2 amp path is left unused: jack + DMIC only. + */ + .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), + .endpoints = jack_dmic_endpoints, + .name_prefix = "rt712" + } +}; + +/* + * Unique ID 0xC drives the left speaker and 0x9 the right one. The order of the + * entries matters as much as the endpoints: asoc_sdw_parse_sdw_endpoints() + * appends them to the dailink in array order and sdw_compute_slave_ports() + * hands out payload block offsets in that same order, so the first entry is the + * one that receives channel 0. + */ +static const struct snd_soc_acpi_adr_device tas2783x2_l0u9c_adr[] = { + { + .adr = 0x00003C0102000001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "tas2783-1", + }, + { + .adr = 0x0000390102000001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "tas2783-2", + }, +}; + +/* HP OmniBook X Flip 14-kc0xxx (board 8EA1) */ +static const struct snd_soc_acpi_link_adr acp70_tas2783x2_l0u9c_rt712_vb_l1u0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(tas2783x2_l0u9c_adr), + .adr_d = tas2783x2_l0u9c_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt712_vb_l1u0_adr), + .adr_d = rt712_vb_l1u0_adr, + }, + {} +}; + static const struct snd_soc_acpi_endpoint rt721_endpoints[] = { { /* Jack Playback/Capture Endpoint (AIF1) */ .num = 0, @@ -706,6 +756,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { }, { .link_mask = BIT(0) | BIT(1), + .links = acp70_tas2783x2_l0u9c_rt712_vb_l1u0, + .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb, + .drv_name = "amd_sdw", + }, + { + .link_mask = BIT(0) | BIT(1), .links = acp70_rt1320_l0_rt722_l1, .drv_name = "amd_sdw", }, diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h index 5b6362103ca0..b602a983feb7 100644 --- a/sound/soc/amd/mach-config.h +++ b/sound/soc/amd/mach-config.h @@ -28,6 +28,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[]; struct config_entry { u32 flags; diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c index 352485dd98b1..b0147e88ba54 100644 --- a/sound/soc/amd/raven/acp3x-i2s.c +++ b/sound/soc/amd/raven/acp3x-i2s.c @@ -250,11 +250,17 @@ static int acp3x_i2s_trigger(struct snd_pcm_substream *substream, return ret; } +static const u64 acp3x_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A; + static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = { .hw_params = acp3x_i2s_hwparams, .trigger = acp3x_i2s_trigger, .set_fmt = acp3x_i2s_set_fmt, .set_tdm_slot = acp3x_i2s_set_tdm_slot, + .auto_selectable_formats = &acp3x_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_component_driver acp3x_dai_component = { diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c index bf719f628617..dbfb87e2fe92 100644 --- a/sound/soc/amd/vangogh/acp5x-i2s.c +++ b/sound/soc/amd/vangogh/acp5x-i2s.c @@ -337,11 +337,17 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, return ret; } +static const u64 acp5x_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A; + static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = { .hw_params = acp5x_i2s_hwparams, .trigger = acp5x_i2s_trigger, .set_fmt = acp5x_i2s_set_fmt, .set_tdm_slot = acp5x_i2s_set_tdm_slot, + .auto_selectable_formats = &acp5x_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_component_driver acp5x_dai_component = { diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index ae90e9f513d1..3f852393fc99 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -795,12 +795,12 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { } }, { - .driver_data = &acp6x_card, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), - } - }, + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), + } + }, { .driver_data = &acp6x_card, .matches = { diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 492165c0e1ea..be702b2942a5 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -133,12 +133,17 @@ struct mca_cluster { struct clk *clk_parent; struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; - bool port_started[SNDRV_PCM_STREAM_LAST + 1]; - int port_driver; /* The cluster driving this cluster's port */ + bool clk_provider; + + bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1]; + int port_clk_driver; /* The cluster driving this cluster's port */ bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; struct device_link *pd_link; + /* In case of clock consumer FE */ + int syncgen_in_use; + unsigned int bclk_ratio; /* Masks etc. picked up via the set_tdm_slot method */ @@ -157,7 +162,7 @@ struct mca_data { struct reset_control *rstc; struct device_link *pd_link; - /* Mutex for accessing port_driver of foreign clusters */ + /* Mutex for accessing port_clk_driver of foreign clusters */ struct mutex port_mutex; int nclusters; @@ -210,16 +215,22 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, SERDES_STATUS_EN | SERDES_STATUS_RST, SERDES_STATUS_RST); /* - * Experiments suggest that it takes at most ~1 us - * for the bit to clear, so wait 2 us for good measure. + * The SERDES cluster needs a bit of time to reset itself + * and settle before we start poking it. This is... slow... */ - udelay(2); + udelay(25); WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) & SERDES_STATUS_RST); mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, FIELD_PREP(SERDES_CONF_SYNC_SEL, 0)); mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1)); + /* + * ADMAC gets started right after this. This delay seems + * to be needed for that to be reliable, e.g. ensure the + * clock is stable? + */ + udelay(100); break; default: break; @@ -256,11 +267,29 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int mca_fe_get_portmask(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); + struct snd_soc_dpcm *dpcm; + int mask = 0; + + for_each_dpcm_be(fe, substream->stream, dpcm) { + int no = mca_dai_to_cluster(snd_soc_rtd_to_cpu(dpcm->be, 0))->no; + + mask |= 1 << no; + } + + return mask; +} + static int mca_fe_enable_clocks(struct mca_cluster *cl) { struct mca_data *mca = cl->host; int ret; + if (!cl->clk_provider) + return -EINVAL; + ret = clk_prepare_enable(cl->clk_parent); if (ret) { dev_err(mca->dev, @@ -311,7 +340,7 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) for (i = 0; i < mca->nclusters; i++) { be_cl = &mca->clusters[i]; - if (be_cl->port_driver != cl->no) + if (be_cl->port_clk_driver != cl->no) continue; for_each_pcm_streams(stream) { @@ -323,59 +352,79 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) return false; } -static int mca_be_prepare(struct snd_pcm_substream *substream, +static int mca_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mca_cluster *cl = mca_dai_to_cluster(dai); struct mca_data *mca = cl->host; struct mca_cluster *fe_cl; - int ret; - if (cl->port_driver < 0) + if (cl->port_clk_driver < 0) return -EINVAL; - fe_cl = &mca->clusters[cl->port_driver]; - /* - * Typically the CODECs we are paired with will require clocks - * to be present at time of unmute with the 'mute_stream' op - * or at time of DAPM widget power-up. We need to enable clocks - * here at the latest (frontend prepare would be too late). + * We are operating on a foreign cluster here, but since we + * belong to the same PCM, accesses should have been + * synchronized at ASoC level. */ - if (!mca_fe_clocks_in_use(fe_cl)) { - ret = mca_fe_enable_clocks(fe_cl); - if (ret < 0) - return ret; - } + fe_cl = &mca->clusters[cl->port_clk_driver]; + if (!mca_fe_clocks_in_use(fe_cl)) + return 0; /* Nothing to do */ - cl->clocks_in_use[substream->stream] = true; + cl->clocks_in_use[substream->stream] = false; + + if (!mca_fe_clocks_in_use(fe_cl)) + mca_fe_disable_clocks(fe_cl); return 0; } -static int mca_be_hw_free(struct snd_pcm_substream *substream, +static int mca_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mca_cluster *cl = mca_dai_to_cluster(dai); struct mca_data *mca = cl->host; - struct mca_cluster *fe_cl; - if (cl->port_driver < 0) - return -EINVAL; + if (cl->clk_provider) + return 0; - /* - * We are operating on a foreign cluster here, but since we - * belong to the same PCM, accesses should have been - * synchronized at ASoC level. - */ - fe_cl = &mca->clusters[cl->port_driver]; - if (!mca_fe_clocks_in_use(fe_cl)) - return 0; /* Nothing to do */ + /* Turn on the cluster power domain if not already in use */ + if (!cl->syncgen_in_use) { + int port = ffs(mca_fe_get_portmask(substream)); - cl->clocks_in_use[substream->stream] = false; + cl->pd_link = device_link_add(mca->dev, cl->pd_dev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!cl->pd_link) { + dev_err(mca->dev, + "cluster %d: unable to prop-up power domain\n", cl->no); + return -EINVAL; + } - if (!mca_fe_clocks_in_use(fe_cl)) - mca_fe_disable_clocks(fe_cl); + mca_modify(cl, REG_SYNCGEN_MCLK_SEL, SYNCGEN_MCLK_SEL, BIT(port)); + mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, + SYNCGEN_STATUS_EN); + } + cl->syncgen_in_use |= 1 << substream->stream; + + return 0; +} + +static int mca_fe_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mca_cluster *cl = mca_dai_to_cluster(dai); + + if (cl->clk_provider) + return 0; + + cl->syncgen_in_use &= ~(1 << substream->stream); + if (cl->syncgen_in_use) + return 0; + + mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); + if (cl->pd_link) + device_link_del(cl->pd_link); return 0; } @@ -390,7 +439,7 @@ static unsigned int mca_crop_mask(unsigned int mask, int nchans) static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, unsigned int mask, int slots, int nchans, - int slot_width, bool is_tx, int port) + int slot_width, bool is_tx, int portmask) { __iomem void *serdes_base = cl->base + serdes_unit; u32 serdes_conf, serdes_conf_mask; @@ -449,7 +498,7 @@ static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, serdes_base + REG_RX_SERDES_SLOTMASK); writel_relaxed(~((u32)mca_crop_mask(mask, nchans)), serdes_base + REG_RX_SERDES_SLOTMASK + 0x4); - writel_relaxed(1 << port, + writel_relaxed(portmask, serdes_base + REG_RX_SERDES_PORT); } @@ -505,9 +554,18 @@ static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) u32 serdes_conf = 0; u32 bitstart; - if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != - SND_SOC_DAIFMT_BP_FP) + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BP_FP: + cl->clk_provider = true; + break; + + case SND_SOC_DAIFMT_BC_FC: + cl->clk_provider = false; + break; + + default: goto err; + } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: @@ -564,24 +622,6 @@ static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) return 0; } -static int mca_fe_get_port(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); - struct snd_soc_pcm_runtime *be; - struct snd_soc_dpcm *dpcm; - - be = NULL; - for_each_dpcm_be(fe, substream->stream, dpcm) { - be = dpcm->be; - break; - } - - if (!be) - return -EINVAL; - - return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; -} - static int mca_fe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -595,7 +635,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, unsigned long bclk_ratio; unsigned int tdm_slots, tdm_slot_width, tdm_mask; u32 regval, pad; - int ret, port, nchans_ceiled; + int ret, portmask, nchans_ceiled; if (!cl->tdm_slot_width) { /* @@ -644,13 +684,13 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, tdm_mask = (1 << tdm_slots) - 1; } - port = mca_fe_get_port(substream); - if (port < 0) - return port; + portmask = mca_fe_get_portmask(substream); + if (!portmask) + return -EINVAL; ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF, tdm_mask, tdm_slots, params_channels(params), - tdm_slot_width, is_tx, port); + tdm_slot_width, is_tx, portmask); if (ret) return ret; @@ -699,6 +739,19 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, return 0; } +static const u64 mca_fe_selectable_formats[] = +{ + /* pattern 1 */ + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF, + + /* pattern 2 */ + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF +}; + static const struct snd_soc_dai_ops mca_fe_ops = { .startup = mca_fe_startup, .set_fmt = mca_fe_set_fmt, @@ -706,66 +759,128 @@ static const struct snd_soc_dai_ops mca_fe_ops = { .set_tdm_slot = mca_fe_set_tdm_slot, .hw_params = mca_fe_hw_params, .trigger = mca_fe_trigger, + .prepare = mca_fe_prepare, + .hw_free = mca_fe_hw_free, + .auto_selectable_formats = mca_fe_selectable_formats, + .num_auto_selectable_formats = ARRAY_SIZE(mca_fe_selectable_formats), }; -static bool mca_be_started(struct mca_cluster *cl) +/* + * Is there a FE attached which will be feeding this port's clocks? + */ +static bool mca_be_clk_started(struct mca_cluster *cl) { int stream; for_each_pcm_streams(stream) - if (cl->port_started[stream]) + if (cl->port_clk_started[stream]) return true; return false; } -static int mca_be_startup(struct snd_pcm_substream *substream, +static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be, + int stream) +{ + struct snd_soc_pcm_runtime *fe = NULL; + struct snd_soc_dpcm *dpcm; + + for_each_dpcm_fe(be, stream, dpcm) { + if (fe && dpcm->fe != fe) { + dev_err(be->dev, "many FE per one BE unsupported\n"); + return NULL; + } + + fe = dpcm->fe; + } + + return fe; +} + +static int mca_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); - struct snd_soc_pcm_runtime *fe; + struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); struct mca_cluster *cl = mca_dai_to_cluster(dai); - struct mca_cluster *fe_cl; struct mca_data *mca = cl->host; - struct snd_soc_dpcm *dpcm; + struct mca_cluster *fe_cl, *fe_clk_cl; + int ret; - fe = NULL; + fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); - for_each_dpcm_fe(be, substream->stream, dpcm) { - if (fe && dpcm->fe != fe) { - dev_err(mca->dev, "many FE per one BE unsupported\n"); - return -EINVAL; - } + if (!fe_cl->clk_provider) + return 0; - fe = dpcm->fe; + if (cl->port_clk_driver < 0) + return 0; + + fe_clk_cl = &mca->clusters[cl->port_clk_driver]; + + /* + * Typically the CODECs we are paired with will require clocks + * to be present at time of unmute with the 'mute_stream' op + * or at time of DAPM widget power-up. We need to enable clocks + * here at the latest (frontend prepare would be too late). + */ + if (!mca_fe_clocks_in_use(fe_clk_cl)) { + ret = mca_fe_enable_clocks(fe_clk_cl); + if (ret < 0) + return ret; } + cl->clocks_in_use[substream->stream] = true; + + return 0; +} + +static int mca_be_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); + struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_cluster *fe_cl; + struct mca_data *mca = cl->host; + if (!fe) return -EINVAL; - fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); - if (mca_be_started(cl)) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), + cl->base + REG_PORT_DATA_SEL); + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, + PORT_ENABLES_TX_DATA); + } + + if (!fe_cl->clk_provider) + return 0; + + if (mca_be_clk_started(cl)) { /* * Port is already started in the other direction. * Make sure there isn't a conflict with another cluster - * driving the port. + * driving the port clocks. */ - if (cl->port_driver != fe_cl->no) + if (cl->port_clk_driver != fe_cl->no) return -EINVAL; - cl->port_started[substream->stream] = true; + cl->port_clk_started[substream->stream] = true; return 0; } - writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA, - cl->base + REG_PORT_ENABLES); writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1), cl->base + REG_PORT_CLOCK_SEL); + writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), cl->base + REG_PORT_DATA_SEL); + + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, + PORT_ENABLES_CLOCKS); + scoped_guard(mutex, &mca->port_mutex) - cl->port_driver = fe_cl->no; - cl->port_started[substream->stream] = true; + cl->port_clk_driver = fe_cl->no; + cl->port_clk_started[substream->stream] = true; return 0; } @@ -773,20 +888,34 @@ static int mca_be_startup(struct snd_pcm_substream *substream, static void mca_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_cluster *fe_cl; struct mca_data *mca = cl->host; - cl->port_started[substream->stream] = false; + if (!fe) + return; + fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); - if (!mca_be_started(cl)) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0); + writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); + } + + if (!fe_cl->clk_provider) + return; + + cl->port_clk_started[substream->stream] = false; + if (!mca_be_clk_started(cl)) { /* * Were we the last direction to shutdown? - * Turn off the lights. + * Turn off the lights (clocks). */ - writel_relaxed(0, cl->base + REG_PORT_ENABLES); - writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, 0); + writel_relaxed(0, cl->base + REG_PORT_CLOCK_SEL); scoped_guard(mutex, &mca->port_mutex) - cl->port_driver = -1; + cl->port_clk_driver = -1; } } @@ -1092,7 +1221,7 @@ static int apple_mca_probe(struct platform_device *pdev) cl->host = mca; cl->no = i; cl->base = base + CLUSTER_STRIDE * i; - cl->port_driver = -1; + cl->port_clk_driver = -1; cl->clk_parent = of_clk_get(pdev->dev.of_node, i); if (IS_ERR(cl->clk_parent)) { dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n", diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c index 762199faf872..d9ad262ec44d 100644 --- a/sound/soc/atmel/atmel-i2s.c +++ b/sound/soc/atmel/atmel-i2s.c @@ -540,12 +540,16 @@ static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const u64 atmel_i2s_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_I2S; + static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { .probe = atmel_i2s_dai_probe, .prepare = atmel_i2s_prepare, .trigger = atmel_i2s_trigger, .hw_params = atmel_i2s_hw_params, .set_fmt = atmel_i2s_set_dai_fmt, + .auto_selectable_formats = &atmel_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver atmel_i2s_dai = { diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 89098f41679c..f4f886bf678d 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -825,6 +825,11 @@ static int atmel_ssc_resume(struct snd_soc_component *component) #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S32_LE) +static const u64 atmel_ssc_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A; + static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { .startup = atmel_ssc_startup, .shutdown = atmel_ssc_shutdown, @@ -833,6 +838,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { .hw_params = atmel_ssc_hw_params, .set_fmt = atmel_ssc_set_dai_fmt, .set_clkdiv = atmel_ssc_set_dai_clkdiv, + .auto_selectable_formats = &atmel_ssc_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver atmel_ssc_dai = { diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index 0f4021c6c588..df3e5ef8fa61 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -82,32 +82,33 @@ static struct snd_soc_card atmel_asoc_wm8904_card = { static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct device_node *codec_np, *cpu_np; struct snd_soc_card *card = &atmel_asoc_wm8904_card; struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; int ret; if (!np) { - dev_err(&pdev->dev, "only device tree supported\n"); + dev_err(dev, "only device tree supported\n"); return -EINVAL; } ret = snd_soc_of_parse_card_name(card, "atmel,model"); if (ret) { - dev_err(&pdev->dev, "failed to parse card name\n"); + dev_err(dev, "failed to parse card name\n"); return ret; } ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); if (ret) { - dev_err(&pdev->dev, "failed to parse audio routing\n"); + dev_err(dev, "failed to parse audio routing\n"); return ret; } cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); if (!cpu_np) { - dev_err(&pdev->dev, "failed to get dai and pcm info\n"); + dev_err(dev, "failed to get dai and pcm info\n"); ret = -EINVAL; return ret; } @@ -117,7 +118,7 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); if (!codec_np) { - dev_err(&pdev->dev, "failed to get codec info\n"); + dev_err(dev, "failed to get codec info\n"); ret = -EINVAL; return ret; } diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c index 17d138bb9064..a832dee73ac7 100644 --- a/sound/soc/atmel/mchp-i2s-mcc.c +++ b/sound/soc/atmel/mchp-i2s-mcc.c @@ -912,6 +912,13 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) return 0; } +static const u64 mchp_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_GATED | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { .probe = mchp_i2s_mcc_dai_probe, .set_sysclk = mchp_i2s_mcc_set_sysclk, @@ -922,6 +929,8 @@ static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { .hw_free = mchp_i2s_mcc_hw_free, .set_fmt = mchp_i2s_mcc_set_dai_fmt, .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, + .auto_selectable_formats = &mchp_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; #define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000 diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index ec7233ce1f78..a6a9b5e6cd95 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -741,6 +741,8 @@ static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, return ret; } +static const u64 mchp_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_PDM; + static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { .probe = mchp_pdmc_dai_probe, .set_fmt = mchp_pdmc_set_fmt, @@ -748,6 +750,8 @@ static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { .hw_params = mchp_pdmc_hw_params, .trigger = mchp_pdmc_trigger, .pcm_new = &mchp_pdmc_pcm_new, + .auto_selectable_formats = &mchp_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver mchp_pdmc_dai = { diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index 81abe2e18402..78346cd7959c 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -42,7 +42,7 @@ static const struct platform_device_id db1200_pids[] = { .name = "db1550-i2s", .driver_data = 5, }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, db1200_pids); diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c index 57735004f416..0e9c7eef9ee8 100644 --- a/sound/soc/au1x/i2sc.c +++ b/sound/soc/au1x/i2sc.c @@ -202,11 +202,22 @@ static int au1xi2s_startup(struct snd_pcm_substream *substream, return 0; } +static const u64 au1xi2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops au1xi2s_dai_ops = { .startup = au1xi2s_startup, .trigger = au1xi2s_trigger, .hw_params = au1xi2s_hw_params, .set_fmt = au1xi2s_set_fmt, + .auto_selectable_formats = &au1xi2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver au1xi2s_dai_driver = { diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 94698e08a513..0f9d80883116 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -210,9 +210,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, { struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); unsigned long r, ro, stat; - int chans, t, stype = substream->stream; - - chans = params_channels(params); + int t, stype = substream->stream; r = ro = __raw_readl(AC97_CFG(pscdata)); stat = __raw_readl(AC97_STAT(pscdata)); diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index bf59105fcb7a..72aa5177147f 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -262,11 +262,22 @@ static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream, return 0; } +static const u64 au1xpsc_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { .startup = au1xpsc_i2s_startup, .trigger = au1xpsc_i2s_trigger, .hw_params = au1xpsc_i2s_hw_params, .set_fmt = au1xpsc_i2s_set_fmt, + .auto_selectable_formats = &au1xpsc_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = { diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 87d2f06c2f53..208977828254 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -748,6 +748,17 @@ static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const u64 bcm2835_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .probe = bcm2835_i2s_dai_probe, .startup = bcm2835_i2s_startup, @@ -758,6 +769,8 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .set_fmt = bcm2835_i2s_set_dai_fmt, .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, + .auto_selectable_formats = &bcm2835_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver bcm2835_i2s_dai = { diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c index 47706ae0a31f..753021789a52 100644 --- a/sound/soc/bcm/cygnus-ssp.c +++ b/sound/soc/bcm/cygnus-ssp.c @@ -1133,6 +1133,11 @@ static int cygnus_ssp_resume(struct snd_soc_component *component) #define cygnus_ssp_resume NULL #endif +static const u64 cygnus_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B; + static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { .startup = cygnus_ssp_startup, .shutdown = cygnus_ssp_shutdown, @@ -1141,6 +1146,8 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { .set_fmt = cygnus_ssp_set_fmt, .set_sysclk = cygnus_ssp_set_sysclk, .set_tdm_slot = cygnus_set_dai_tdm_slot, + .auto_selectable_formats = &cygnus_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 5dba741594fa..8195ed7c6dd6 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -401,6 +401,15 @@ static int ep93xx_i2s_resume(struct snd_soc_component *component) #define ep93xx_i2s_resume NULL #endif +static const u64 ep93xx_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { .probe = ep93xx_i2s_dai_probe, .startup = ep93xx_i2s_startup, @@ -408,6 +417,8 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { .hw_params = ep93xx_i2s_hw_params, .set_sysclk = ep93xx_i2s_set_sysclk, .set_fmt = ep93xx_i2s_set_dai_fmt, + .auto_selectable_formats = &ep93xx_selectable_formats, + .num_auto_selectable_formats = 1, }; #define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d3298dcbac08..f9a47e262a77 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -238,6 +238,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT715_SDCA_SDW imply SND_SOC_RT721_SDCA_SDW imply SND_SOC_RT722_SDCA_SDW + imply SND_SOC_RT766_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW imply SND_SOC_RT1318 @@ -367,6 +368,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WSA881X imply SND_SOC_WSA883X imply SND_SOC_WSA884X + imply SND_SOC_WSA885X imply SND_SOC_ZL38060 help Normally ASoC codec drivers are only built if a machine driver which @@ -615,8 +617,8 @@ config SND_SOC_AK4613 depends on I2C config SND_SOC_AK4619 - tristate "AKM AK4619 CODEC" - depends on I2C + tristate "AKM AK4619 CODEC" + depends on I2C config SND_SOC_AK4642 tristate "AKM AK4642 CODEC" @@ -894,6 +896,7 @@ config SND_SOC_CS35L56_SDW tristate "Cirrus Logic CS35L56 CODEC (SDW)" depends on SOUNDWIRE select REGMAP_SOUNDWIRE + select IRQ_DOMAIN select SND_SOC_CS35L56 select SND_SOC_CS35L56_SHARED help @@ -1206,16 +1209,16 @@ config SND_SOC_JZ4725B_CODEC will be called snd-soc-jz4725b-codec. config SND_SOC_JZ4760_CODEC - depends on MACH_INGENIC || COMPILE_TEST - depends on OF - select REGMAP - tristate "Ingenic JZ4760 internal CODEC" - help - Enable support for the internal CODEC found in the JZ4760 SoC - from Ingenic. + depends on MACH_INGENIC || COMPILE_TEST + depends on OF + select REGMAP + tristate "Ingenic JZ4760 internal CODEC" + help + Enable support for the internal CODEC found in the JZ4760 SoC + from Ingenic. - This driver can also be built as a module. If so, the module - will be called snd-soc-jz4760-codec. + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4760-codec. config SND_SOC_JZ4770_CODEC depends on MACH_INGENIC || COMPILE_TEST @@ -1893,7 +1896,7 @@ config SND_SOC_RT5670 depends on I2C config SND_SOC_RT5677 - tristate + tristate "Realtek RT5677 Codec" depends on I2C select REGMAP_I2C select REGMAP_IRQ @@ -1983,6 +1986,15 @@ config SND_SOC_RT715_SDCA_SDW select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ +config SND_SOC_RT766_SDCA_SDW + tristate "Realtek RT766 SDCA Codec - SDW" + depends on SOUNDWIRE + depends on SND_SOC_SDCA + select SND_SOC_SDCA_HID + select SND_SOC_SDCA_IRQ + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_RT9120 tristate "Richtek RT9120 Stereo Class-D Amplifier" depends on I2C @@ -2302,8 +2314,8 @@ config SND_SOC_TLV320ADC3XXX depends on I2C depends on GPIOLIB help - Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 - ADCs. + Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 + ADCs. config SND_SOC_TLV320AIC23 tristate @@ -2776,6 +2788,16 @@ config SND_SOC_WSA884X This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D Smart Speaker Amplifier. +config SND_SOC_WSA885X + tristate "WSA885X Codec" + depends on I2C + select REGMAP_I2C + help + This enables support for Qualcomm WSA885X Stereo Smart Speaker + Amplifier. The codec driver programs the amplifier register + map and exposes the DAI and mixer controls used by Qualcomm + audio machine drivers. + config SND_SOC_ZL38060 tristate "Microsemi ZL38060 Connected Home Audio Processor" depends on SPI_MASTER @@ -2899,28 +2921,32 @@ config SND_SOC_TPA6130A2 depends on I2C config SND_SOC_LPASS_MACRO_COMMON - tristate + tristate config SND_SOC_LPASS_WSA_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" config SND_SOC_LPASS_VA_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)" config SND_SOC_LPASS_RX_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)" config SND_SOC_LPASS_TX_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d2a689006d69..3d122ace75ad 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -285,6 +285,7 @@ snd-soc-rt715-y := rt715.o rt715-sdw.o snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o +snd-soc-rt766-sdca-y := rt766-sdca.o rt766-sdca-sdw.o snd-soc-rt9120-y := rt9120.o snd-soc-rt9123-y := rt9123.o snd-soc-rt9123p-y := rt9123p.o @@ -427,6 +428,7 @@ snd-soc-wm-hubs-y := wm_hubs.o snd-soc-wsa881x-y := wsa881x.o snd-soc-wsa883x-y := wsa883x.o snd-soc-wsa884x-y := wsa884x.o +snd-soc-wsa885x-y := wsa885x.o snd-soc-zl38060-y := zl38060.o # Amp snd-soc-max9877-y := max9877.o @@ -726,6 +728,7 @@ obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o +obj-$(CONFIG_SND_SOC_RT766_SDCA_SDW) += snd-soc-rt766-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o @@ -878,6 +881,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o +obj-$(CONFIG_SND_SOC_WSA885X) += snd-soc-wsa885x.o obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o # Amp diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 6e8ef9cd1b31..2cf96cbdd294 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -14,6 +14,7 @@ * for ST-Ericsson. */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> @@ -989,9 +990,8 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - mutex_lock(&drvdata->ctrl_lock); + guard(mutex)(&drvdata->ctrl_lock); ucontrol->value.enumerated.item[0] = drvdata->sid_status; - mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -1014,7 +1014,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, return -EIO; } - mutex_lock(&drvdata->ctrl_lock); + guard(mutex)(&drvdata->ctrl_lock); sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { @@ -1025,7 +1025,8 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, } else { status = -EBUSY; } - goto out; + dev_dbg(component->dev, "%s: Exit\n", __func__); + return status; } snd_soc_component_write(component, AB8500_SIDFIRADR, 0); @@ -1043,9 +1044,6 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, drvdata->sid_status = SID_FIR_CONFIGURED; -out: - mutex_unlock(&drvdata->ctrl_lock); - dev_dbg(component->dev, "%s: Exit\n", __func__); return status; diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 8afeadcaf8b0..dbea83bd49b6 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -193,9 +193,15 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, return 0; } +static const u64 ad1836_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops ad1836_dai_ops = { .hw_params = ad1836_hw_params, .set_fmt = ad1836_set_dai_fmt, + .auto_selectable_formats = &ad1836_selectable_formats, + .num_auto_selectable_formats = 1, }; #define AD183X_DAI(_name, num_dacs, num_adcs) \ diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index b93531c3a9a4..9841c779b175 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -394,6 +394,14 @@ static int ad193x_startup(struct snd_pcm_substream *substream, &constr); } +static const u64 ad193x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops ad193x_dai_ops = { .startup = ad193x_startup, .hw_params = ad193x_hw_params, @@ -401,6 +409,8 @@ static const struct snd_soc_dai_ops ad193x_dai_ops = { .set_tdm_slot = ad193x_set_tdm_slot, .set_sysclk = ad193x_set_dai_sysclk, .set_fmt = ad193x_set_dai_fmt, + .auto_selectable_formats = &ad193x_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index a70c46dd5d76..27ce03b0fb91 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -68,23 +68,33 @@ #define ADAU1761_FIRMWARE "adau1761.bin" static const struct reg_default adau1761_reg_defaults[] = { - { ADAU1761_DEJITTER, 0x03 }, + { ADAU17X1_CLOCK_CONTROL, 0x00 }, + { ADAU17X1_PLL_CONTROL, 0x00 }, { ADAU1761_DIGMIC_JACKDETECT, 0x00 }, + { ADAU17X1_REC_POWER_MGMT, 0x00 }, { ADAU1761_REC_MIXER_LEFT0, 0x00 }, { ADAU1761_REC_MIXER_LEFT1, 0x00 }, { ADAU1761_REC_MIXER_RIGHT0, 0x00 }, { ADAU1761_REC_MIXER_RIGHT1, 0x00 }, { ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 }, + { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 }, + { ADAU17X1_MICBIAS, 0x00 }, { ADAU1761_ALC_CTRL0, 0x00 }, { ADAU1761_ALC_CTRL1, 0x00 }, { ADAU1761_ALC_CTRL2, 0x00 }, { ADAU1761_ALC_CTRL3, 0x00 }, - { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 }, - { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 }, + { ADAU17X1_SERIAL_PORT0, 0x00 }, + { ADAU17X1_SERIAL_PORT1, 0x00 }, + { ADAU17X1_CONVERTER0, 0x00 }, + { ADAU17X1_CONVERTER1, 0x00 }, + { ADAU17X1_ADC_CONTROL, 0x00 }, + { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 }, + { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 }, { ADAU1761_PLAY_MIXER_LEFT0, 0x00 }, { ADAU1761_PLAY_MIXER_LEFT1, 0x00 }, { ADAU1761_PLAY_MIXER_RIGHT0, 0x00 }, { ADAU1761_PLAY_MIXER_RIGHT1, 0x00 }, + { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 }, { ADAU1761_PLAY_LR_MIXER_RIGHT, 0x00 }, { ADAU1761_PLAY_MIXER_MONO, 0x00 }, { ADAU1761_PLAY_HP_LEFT_VOL, 0x00 }, @@ -93,20 +103,6 @@ static const struct reg_default adau1761_reg_defaults[] = { { ADAU1761_PLAY_LINE_RIGHT_VOL, 0x00 }, { ADAU1761_PLAY_MONO_OUTPUT_VOL, 0x00 }, { ADAU1761_POP_CLICK_SUPPRESS, 0x00 }, - { ADAU1761_JACK_DETECT_PIN, 0x00 }, - { ADAU1761_CLK_ENABLE0, 0x00 }, - { ADAU1761_CLK_ENABLE1, 0x00 }, - { ADAU17X1_CLOCK_CONTROL, 0x00 }, - { ADAU17X1_PLL_CONTROL, 0x00 }, - { ADAU17X1_REC_POWER_MGMT, 0x00 }, - { ADAU17X1_MICBIAS, 0x00 }, - { ADAU17X1_SERIAL_PORT0, 0x00 }, - { ADAU17X1_SERIAL_PORT1, 0x00 }, - { ADAU17X1_CONVERTER0, 0x00 }, - { ADAU17X1_CONVERTER1, 0x00 }, - { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 }, - { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 }, - { ADAU17X1_ADC_CONTROL, 0x00 }, { ADAU17X1_PLAY_POWER_MGMT, 0x00 }, { ADAU17X1_DAC_CONTROL0, 0x00 }, { ADAU17X1_DAC_CONTROL1, 0x00 }, @@ -114,12 +110,16 @@ static const struct reg_default adau1761_reg_defaults[] = { { ADAU17X1_SERIAL_PORT_PAD, 0xaa }, { ADAU17X1_CONTROL_PORT_PAD0, 0xaa }, { ADAU17X1_CONTROL_PORT_PAD1, 0x00 }, + { ADAU1761_JACK_DETECT_PIN, 0x00 }, + { ADAU1761_DEJITTER, 0x03 }, { ADAU17X1_DSP_SAMPLING_RATE, 0x01 }, { ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 }, { ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 }, { ADAU17X1_DSP_ENABLE, 0x00 }, { ADAU17X1_DSP_RUN, 0x00 }, { ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 }, + { ADAU1761_CLK_ENABLE0, 0x00 }, + { ADAU1761_CLK_ENABLE1, 0x00 }, }; static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1); diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c index 92b226b8b4bb..4342eb7dd9e5 100644 --- a/sound/soc/codecs/adau7118-hw.c +++ b/sound/soc/codecs/adau7118-hw.c @@ -22,7 +22,7 @@ static const struct of_device_id adau7118_of_match[] = { MODULE_DEVICE_TABLE(of, adau7118_of_match); static const struct platform_device_id adau7118_id[] = { - { .name = "adau7118" }, + { .name = "adau7118" }, { } }; MODULE_DEVICE_TABLE(platform, adau7118_id); diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 8a89187f9c78..e97c72ac9b2b 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -743,11 +743,19 @@ static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, adav80x->rate = 0; } +static const u64 adav80x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops adav80x_dai_ops = { .set_fmt = adav80x_set_dai_fmt, .hw_params = adav80x_hw_params, .startup = adav80x_dai_startup, .shutdown = adav80x_dai_shutdown, + .auto_selectable_formats = &adav80x_selectable_formats, + .num_auto_selectable_formats = 1, }; #define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 3e0696b5abf5..be5306d07d40 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -95,6 +95,7 @@ * see * AK4613_ENABLE_TDM_TEST */ +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -384,7 +385,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); struct device *dev = component->dev; - mutex_lock(&priv->lock); + guard(mutex)(&priv->lock); priv->cnt--; if (priv->cnt < 0) { dev_err(dev, "unexpected counter error\n"); @@ -392,7 +393,6 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, } if (!priv->cnt) priv->ctrl1 = 0; - mutex_unlock(&priv->lock); } static void ak4613_hw_constraints(struct ak4613_priv *priv, @@ -507,10 +507,9 @@ 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); + guard(mutex)(&priv->lock); ak4613_hw_constraints(priv, substream); priv->cnt++; - mutex_unlock(&priv->lock); return 0; } @@ -599,45 +598,47 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, */ ret = -EINVAL; - mutex_lock(&priv->lock); - 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--) { - 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; + scoped_guard(mutex, &priv->lock) { + 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--) { + 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; + } } } } - mutex_unlock(&priv->lock); - if (ret < 0) - goto hw_params_end; + if (ret < 0) { + dev_warn(dev, "unsupported data width/format combination\n"); + return ret; + } snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1); snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2); @@ -645,10 +646,6 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic); snd_soc_component_update_bits(component, OCTRL, OCTRL_MASK, priv->oc); -hw_params_end: - if (ret < 0) - dev_warn(dev, "unsupported data width/format combination\n"); - return ret; } diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c index a9063bac2752..819d080b1188 100644 --- a/sound/soc/codecs/arizona-jack.c +++ b/sound/soc/codecs/arizona-jack.c @@ -5,6 +5,7 @@ * Copyright (C) 2012-2014 Wolfson Microelectronics plc */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -707,15 +708,13 @@ static void arizona_micd_timeout_work(struct work_struct *work) struct arizona_priv, micd_timeout_work.work); - mutex_lock(&info->lock); + guard(mutex)(&info->lock); dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); info->detecting = false; arizona_identify_headphone(info); - - mutex_unlock(&info->lock); } static int arizona_micd_adc_read(struct arizona_priv *info) @@ -921,12 +920,11 @@ static void arizona_micd_detect(struct work_struct *work) cancel_delayed_work_sync(&info->micd_timeout_work); - mutex_lock(&info->lock); + guard(mutex)(&info->lock); /* If the cable was removed while measuring ignore the result */ if (!(info->jack->status & SND_JACK_MECHANICAL)) { dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n"); - mutex_unlock(&info->lock); return; } @@ -936,7 +934,6 @@ static void arizona_micd_detect(struct work_struct *work) arizona_button_reading(info); pm_runtime_mark_last_busy(arizona->dev); - mutex_unlock(&info->lock); } static irqreturn_t arizona_micdet(int irq, void *data) @@ -948,10 +945,10 @@ static irqreturn_t arizona_micdet(int irq, void *data) cancel_delayed_work_sync(&info->micd_detect_work); cancel_delayed_work_sync(&info->micd_timeout_work); - mutex_lock(&info->lock); - if (!info->detecting) - debounce = 0; - mutex_unlock(&info->lock); + scoped_guard(mutex, &info->lock) { + if (!info->detecting) + debounce = 0; + } if (debounce) queue_delayed_work(system_power_efficient_wq, @@ -969,9 +966,8 @@ static void arizona_hpdet_work(struct work_struct *work) struct arizona_priv, hpdet_work.work); - mutex_lock(&info->lock); + guard(mutex)(&info->lock); arizona_start_hpdet_acc_id(info); - mutex_unlock(&info->lock); } static int arizona_hpdet_wait(struct arizona_priv *info) @@ -1018,9 +1014,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data) cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work); cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work); - pm_runtime_get_sync(arizona->dev); + guard(pm_runtime_active_auto)(arizona->dev); - mutex_lock(&info->lock); + guard(mutex)(&info->lock); if (info->micd_clamp) { mask = ARIZONA_MICD_CLAMP_STS; @@ -1036,8 +1032,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); if (ret) { dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret); - mutex_unlock(&info->lock); - pm_runtime_put_autosuspend(arizona->dev); return IRQ_NONE; } @@ -1056,62 +1050,61 @@ static irqreturn_t arizona_jackdet(int irq, void *data) &info->micd_timeout_work, msecs_to_jiffies(micd_timeout)); } + } else { + info->last_jackdet = val; - goto out; - } - info->last_jackdet = val; + if (info->last_jackdet == present) { + dev_dbg(arizona->dev, "Detected jack\n"); + snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); - if (info->last_jackdet == present) { - dev_dbg(arizona->dev, "Detected jack\n"); - snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); + info->detecting = true; + info->mic = false; + info->jack_flips = 0; - info->detecting = true; - info->mic = false; - info->jack_flips = 0; + if (!arizona->pdata.hpdet_acc_id) { + arizona_start_mic(info); + } else { + queue_delayed_work(system_power_efficient_wq, + &info->hpdet_work, + msecs_to_jiffies(HPDET_DEBOUNCE)); + } - if (!arizona->pdata.hpdet_acc_id) { - arizona_start_mic(info); + if (info->micd_clamp || !arizona->pdata.jd_invert) + regmap_update_bits(arizona->regmap, + ARIZONA_JACK_DETECT_DEBOUNCE, + ARIZONA_MICD_CLAMP_DB | + ARIZONA_JD1_DB, 0); } else { - queue_delayed_work(system_power_efficient_wq, - &info->hpdet_work, - msecs_to_jiffies(HPDET_DEBOUNCE)); - } + dev_dbg(arizona->dev, "Detected jack removal\n"); - if (info->micd_clamp || !arizona->pdata.jd_invert) - regmap_update_bits(arizona->regmap, - ARIZONA_JACK_DETECT_DEBOUNCE, - ARIZONA_MICD_CLAMP_DB | - ARIZONA_JD1_DB, 0); - } else { - dev_dbg(arizona->dev, "Detected jack removal\n"); - - arizona_stop_mic(info); + arizona_stop_mic(info); - info->num_hpdet_res = 0; - for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) - info->hpdet_res[i] = 0; - info->mic = false; - info->hpdet_done = false; - info->hpdet_retried = false; + info->num_hpdet_res = 0; + for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) + info->hpdet_res[i] = 0; + info->mic = false; + info->hpdet_done = false; + info->hpdet_retried = false; - snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask); + snd_soc_jack_report(info->jack, 0, + ARIZONA_JACK_MASK | info->micd_button_mask); - /* - * If the jack was removed during a headphone detection we - * need to wait for the headphone detection to finish, as - * it can not be aborted. We don't want to be able to start - * a new headphone detection from a fresh insert until this - * one is finished. - */ - arizona_hpdet_wait(info); + /* + * If the jack was removed during a headphone detection we + * need to wait for the headphone detection to finish, as + * it can not be aborted. We don't want to be able to start + * a new headphone detection from a fresh insert until this + * one is finished. + */ + arizona_hpdet_wait(info); - regmap_update_bits(arizona->regmap, - ARIZONA_JACK_DETECT_DEBOUNCE, - ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, - ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); + regmap_update_bits(arizona->regmap, + ARIZONA_JACK_DETECT_DEBOUNCE, + ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, + ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); + } } -out: /* Clear trig_sts to make sure DCVDD is not forced up */ regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG, ARIZONA_MICD_CLAMP_FALL_TRIG_STS | @@ -1119,10 +1112,6 @@ out: ARIZONA_JD1_FALL_TRIG_STS | ARIZONA_JD1_RISE_TRIG_STS); - mutex_unlock(&info->lock); - - pm_runtime_put_autosuspend(arizona->dev); - return IRQ_HANDLED; } diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 8c683b0bb74c..4411165c3734 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -7,6 +7,7 @@ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> */ +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/gcd.h> #include <linux/module.h> @@ -1158,17 +1159,16 @@ int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags) struct arizona_priv *priv = snd_soc_component_get_drvdata(component); int ret = 0; - mutex_lock(&priv->dvfs_lock); + guard(mutex)(&priv->dvfs_lock); if (!priv->dvfs_cached && !priv->dvfs_reqs) { ret = arizona_dvfs_enable(component); if (ret) - goto err; + return ret; } priv->dvfs_reqs |= flags; -err: - mutex_unlock(&priv->dvfs_lock); + return ret; } EXPORT_SYMBOL_GPL(arizona_dvfs_up); @@ -1179,7 +1179,7 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) unsigned int old_reqs; int ret = 0; - mutex_lock(&priv->dvfs_lock); + guard(mutex)(&priv->dvfs_lock); old_reqs = priv->dvfs_reqs; priv->dvfs_reqs &= ~flags; @@ -1187,7 +1187,6 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs) ret = arizona_dvfs_disable(component); - mutex_unlock(&priv->dvfs_lock); return ret; } EXPORT_SYMBOL_GPL(arizona_dvfs_down); @@ -1199,7 +1198,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, struct arizona_priv *priv = snd_soc_component_get_drvdata(component); int ret = 0; - mutex_lock(&priv->dvfs_lock); + guard(mutex)(&priv->dvfs_lock); switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -1222,7 +1221,6 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, break; } - mutex_unlock(&priv->dvfs_lock); return ret; } EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev); @@ -1657,13 +1655,11 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component, { 0x80, 0x0 }, }; - mutex_lock(&arizona->dac_comp_lock); - - dac_comp[1].def = arizona->dac_comp_coeff; - if (rate >= 176400) - dac_comp[2].def = arizona->dac_comp_enabled; - - mutex_unlock(&arizona->dac_comp_lock); + scoped_guard(mutex, &arizona->dac_comp_lock) { + dac_comp[1].def = arizona->dac_comp_coeff; + if (rate >= 176400) + dac_comp[2].def = arizona->dac_comp_enabled; + } regmap_multi_reg_write(arizona->regmap, dac_comp, diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c index 020213e0ca4b..5555c45dffcc 100644 --- a/sound/soc/codecs/aw87390.c +++ b/sound/soc/codecs/aw87390.c @@ -7,6 +7,7 @@ // Author: Weidong Wang <wangweidong.a@awinic.com> // +#include <linux/cleanup.h> #include <linux/i2c.h> #include <linux/firmware.h> #include <linux/regmap.h> @@ -225,11 +226,10 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); int ret; - mutex_lock(&aw87390->lock); + guard(mutex)(&aw87390->lock); ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change\n"); - mutex_unlock(&aw87390->lock); return 0; } @@ -238,8 +238,6 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, aw87390_power_on(aw87390->aw_pa); } - mutex_unlock(&aw87390->lock); - return 1; } @@ -250,7 +248,7 @@ static const struct snd_kcontrol_new aw87390_controls[] = { static int aw87390_request_firmware_file(struct aw87390 *aw87390) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; int ret; aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED; @@ -265,14 +263,11 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev, struct_size(aw87390->aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw87390->aw_cfg) { - release_firmware(cont); + if (!aw87390->aw_cfg) return -ENOMEM; - } aw87390->aw_cfg->len = cont->size; memcpy(aw87390->aw_cfg->data, cont->data, cont->size); - release_firmware(cont); ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg); if (ret) { @@ -280,14 +275,12 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) return ret; } - mutex_lock(&aw87390->lock); + guard(mutex)(&aw87390->lock); ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg); if (ret) dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n"); - mutex_unlock(&aw87390->lock); - return ret; } diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c index d5e886a8f106..3247ba5c71b8 100644 --- a/sound/soc/codecs/aw88081.c +++ b/sound/soc/codecs/aw88081.c @@ -7,6 +7,7 @@ // Author: Weidong Wang <wangweidong.a@awinic.com> // +#include <linux/cleanup.h> #include <linux/firmware.h> #include <linux/i2c.h> #include <linux/regmap.h> @@ -763,9 +764,8 @@ static void aw88081_startup_work(struct work_struct *work) struct aw88081 *aw88081 = container_of(work, struct aw88081, start_work.work); - mutex_lock(&aw88081->lock); + guard(mutex)(&aw88081->lock); aw88081_start_pa(aw88081); - mutex_unlock(&aw88081->lock); } static void aw88081_start(struct aw88081 *aw88081, bool sync_start) @@ -942,11 +942,10 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, int ret; /* pa stop or stopping just set profile */ - mutex_lock(&aw88081->lock); + guard(mutex)(&aw88081->lock); ret = aw88081_dev_set_profile_index(aw88081->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88081->lock); return 0; } @@ -955,8 +954,6 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, aw88081_start(aw88081, AW88081_SYNC_START); } - mutex_unlock(&aw88081->lock); - return 1; } @@ -1136,7 +1133,7 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg static int aw88081_request_firmware_file(struct aw88081 *aw88081) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; struct aw_container *aw_cfg; int ret; @@ -1150,26 +1147,21 @@ static int aw88081_request_firmware_file(struct aw88081 *aw88081) AW88081_ACF_FILE, cont ? cont->size : 0); aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw_cfg) { - release_firmware(cont); + if (!aw_cfg) return -ENOMEM; - } + aw_cfg->len = (int)cont->size; memcpy(aw_cfg->data, cont->data, cont->size); aw88081->aw_cfg = aw_cfg; - release_firmware(cont); - ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg); if (ret) return ret; - mutex_lock(&aw88081->lock); - ret = aw88081_dev_init(aw88081, aw88081->aw_cfg); - mutex_unlock(&aw88081->lock); + guard(mutex)(&aw88081->lock); - return ret; + return aw88081_dev_init(aw88081, aw88081->aw_cfg); } static int aw88081_playback_event(struct snd_soc_dapm_widget *w, @@ -1178,7 +1170,7 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88081->lock); + guard(mutex)(&aw88081->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88081_start(aw88081, AW88081_ASYNC_START); @@ -1189,7 +1181,6 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88081->lock); return 0; } diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c index 3f15f4ac51f7..d2a138211c59 100644 --- a/sound/soc/codecs/aw88166.c +++ b/sound/soc/codecs/aw88166.c @@ -7,6 +7,7 @@ // Author: Weidong Wang <wangweidong.a@awinic.com> // +#include <linux/cleanup.h> #include <linux/crc32.h> #include <linux/firmware.h> #include <linux/gpio/consumer.h> @@ -1173,9 +1174,8 @@ static void aw88166_startup_work(struct work_struct *work) struct aw88166 *aw88166 = container_of(work, struct aw88166, start_work.work); - mutex_lock(&aw88166->lock); + guard(mutex)(&aw88166->lock); aw88166_start_pa(aw88166); - mutex_unlock(&aw88166->lock); } static void aw88166_start(struct aw88166 *aw88166, bool sync_start) @@ -1413,11 +1413,10 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); int ret; - mutex_lock(&aw88166->lock); + guard(mutex)(&aw88166->lock); ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88166->lock); return 0; } @@ -1426,8 +1425,6 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, aw88166_start(aw88166, AW88166_SYNC_START); } - mutex_unlock(&aw88166->lock); - return 1; } @@ -1573,7 +1570,7 @@ static int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg static int aw88166_request_firmware_file(struct aw88166 *aw88166) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; const char *fw_name; int ret; @@ -1593,13 +1590,11 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev, struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw88166->aw_cfg) { - release_firmware(cont); + if (!aw88166->aw_cfg) return -ENOMEM; - } + aw88166->aw_cfg->len = (int)cont->size; memcpy(aw88166->aw_cfg->data, cont->data, cont->size); - release_firmware(cont); ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg); if (ret) { @@ -1607,12 +1602,12 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) return ret; } - mutex_lock(&aw88166->lock); - /* aw device init */ - ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); - if (ret) - dev_err(aw88166->aw_pa->dev, "dev init failed\n"); - mutex_unlock(&aw88166->lock); + scoped_guard(mutex, &aw88166->lock) { + /* aw device init */ + ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); + if (ret) + dev_err(aw88166->aw_pa->dev, "dev init failed\n"); + } return ret; } @@ -1639,7 +1634,7 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88166->lock); + guard(mutex)(&aw88166->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88166_start(aw88166, AW88166_ASYNC_START); @@ -1650,7 +1645,6 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88166->lock); return 0; } diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index 549783d3e75e..b8d06534bc8f 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -8,6 +8,7 @@ // Author: Weidong Wang <wangweidong.a@awinic.com> // +#include <linux/cleanup.h> #include <linux/i2c.h> #include <linux/firmware.h> #include <linux/bitops.h> @@ -206,10 +207,10 @@ static int aw88261_dev_check_sysst(struct aw_device *aw_dev) return ret; check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK) - & AW88261_BIT_SYSST_CHECK; - if (check_val != AW88261_BIT_SYSST_CHECK) { + & AW88261_BIT_PLL_CHECK; + if (check_val != AW88261_BIT_PLL_CHECK) { dev_dbg(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x", - reg_val, AW88261_BIT_SYSST_CHECK); + reg_val, AW88261_BIT_PLL_CHECK); usleep_range(AW88261_2000_US, AW88261_2000_US + 10); } else { return 0; @@ -960,11 +961,10 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, int ret; /* pa stop or stopping just set profile */ - mutex_lock(&aw88261->lock); + guard(mutex)(&aw88261->lock); ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88261->lock); return 0; } @@ -973,8 +973,6 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, aw88261_start(aw88261); } - mutex_unlock(&aw88261->lock); - return 1; } @@ -1038,7 +1036,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88261->lock); + guard(mutex)(&aw88261->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88261_start(aw88261); @@ -1049,7 +1047,6 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88261->lock); return 0; } @@ -1152,7 +1149,7 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg static int aw88261_request_firmware_file(struct aw88261 *aw88261) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; struct aw_container *aw_cfg; const char *fw_name; int ret; @@ -1172,13 +1169,11 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) fw_name, cont ? cont->size : 0); aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw_cfg) { - release_firmware(cont); + if (!aw_cfg) return -ENOMEM; - } + aw_cfg->len = (int)cont->size; memcpy(aw_cfg->data, cont->data, cont->size); - release_firmware(cont); aw88261->aw_cfg = aw_cfg; @@ -1188,12 +1183,12 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) return ret; } - mutex_lock(&aw88261->lock); - /* aw device init */ - ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); - if (ret) - dev_err(aw88261->aw_pa->dev, "dev init failed"); - mutex_unlock(&aw88261->lock); + scoped_guard(mutex, &aw88261->lock) { + /* aw device init */ + ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); + if (ret) + dev_err(aw88261->aw_pa->dev, "dev init failed"); + } return ret; } diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h index 270ccf375f36..f16f9f8c40a2 100644 --- a/sound/soc/codecs/aw88261.h +++ b/sound/soc/codecs/aw88261.h @@ -194,12 +194,6 @@ AW88261_OTHS_OT_VALUE | \ AW88261_PLLS_LOCKED_VALUE)) -#define AW88261_BIT_SYSST_CHECK \ - (AW88261_BSTS_FINISHED_VALUE | \ - AW88261_SWS_SWITCHING_VALUE | \ - AW88261_CLKS_STABLE_VALUE | \ - AW88261_PLLS_LOCKED_VALUE) - #define AW88261_ULS_HMUTE_START_BIT (14) #define AW88261_ULS_HMUTE_BITS_LEN (1) #define AW88261_ULS_HMUTE_MASK \ diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index ee0e8bd8c54c..2f4108624d13 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -8,6 +8,7 @@ // Author: Weidong Wang <wangweidong.a@awinic.com> // +#include <linux/cleanup.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/firmware.h> @@ -51,9 +52,8 @@ static void aw88395_startup_work(struct work_struct *work) struct aw88395 *aw88395 = container_of(work, struct aw88395, start_work.work); - mutex_lock(&aw88395->lock); + guard(mutex)(&aw88395->lock); aw88395_start_pa(aw88395); - mutex_unlock(&aw88395->lock); } static void aw88395_start(struct aw88395 *aw88395, bool sync_start) @@ -224,11 +224,10 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, int ret; /* pa stop or stopping just set profile */ - mutex_lock(&aw88395->lock); + guard(mutex)(&aw88395->lock); ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]); if (ret < 0) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88395->lock); return 0; } @@ -237,8 +236,6 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, aw88395_start(aw88395, AW88395_SYNC_START); } - mutex_unlock(&aw88395->lock); - return 1; } @@ -366,7 +363,7 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88395->lock); + guard(mutex)(&aw88395->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88395_start(aw88395, AW88395_ASYNC_START); @@ -377,7 +374,6 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88395->lock); return 0; } @@ -461,7 +457,7 @@ static void aw88395_hw_reset(struct aw88395 *aw88395) static int aw88395_request_firmware_file(struct aw88395 *aw88395) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; struct aw_container *aw_cfg; int ret; @@ -477,13 +473,11 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) AW88395_ACF_FILE, cont ? cont->size : 0); aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw_cfg) { - release_firmware(cont); + if (!aw_cfg) return -ENOMEM; - } + aw_cfg->len = (int)cont->size; memcpy(aw_cfg->data, cont->data, cont->size); - release_firmware(cont); aw88395->aw_cfg = aw_cfg; @@ -495,12 +489,12 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__); - mutex_lock(&aw88395->lock); - /* aw device init */ - ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); - if (ret < 0) - dev_err(aw88395->aw_pa->dev, "dev init failed"); - mutex_unlock(&aw88395->lock); + scoped_guard(mutex, &aw88395->lock) { + /* aw device init */ + ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); + if (ret < 0) + dev_err(aw88395->aw_pa->dev, "dev init failed"); + } return ret; } diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c index 79c3135a4110..12d42d27560d 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.c +++ b/sound/soc/codecs/aw88395/aw88395_device.c @@ -8,6 +8,7 @@ // Author: Ben Yi <yijiangtao@awinic.com> // +#include <linux/cleanup.h> #include <linux/crc32.h> #include <linux/i2c.h> #include <linux/minmax.h> @@ -70,7 +71,7 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, u32 reg_value; int ret; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); switch (data_type) { case AW_DSP_16_DATA: ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); @@ -93,7 +94,6 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, /* clear dsp chip select state*/ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); - mutex_unlock(&aw_dev->dsp_lock); return ret; } @@ -156,7 +156,7 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, u32 reg_value; int ret; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); switch (data_type) { case AW_DSP_16_DATA: ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); @@ -179,7 +179,6 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, /* clear dsp chip select state*/ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); - mutex_unlock(&aw_dev->dsp_lock); return ret; } @@ -1110,42 +1109,36 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev, #ifdef AW88395_DSP_I2C_WRITES u32 tmp_len; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); if (ret) - goto error_operation; + return ret; for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { tmp_len = min(len - i, AW88395_MAX_RAM_WRITE_BYTE_SIZE); ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, &data[i], tmp_len); if (ret) - goto error_operation; + return ret; } - mutex_unlock(&aw_dev->dsp_lock); #else __be16 reg_val; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); /* i2c write */ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); if (ret) - goto error_operation; + return ret; for (i = 0; i < len; i += 2) { reg_val = cpu_to_be16p((u16 *)(data + i)); ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)reg_val); if (ret) - goto error_operation; + return ret; } - mutex_unlock(&aw_dev->dsp_lock); #endif return 0; - -error_operation: - mutex_unlock(&aw_dev->dsp_lock); - return ret; } static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, @@ -1231,14 +1224,14 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) { unsigned int reg_val; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); /* check the odd bits of reg 0x40 */ regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST); regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]", reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); - goto error; + return -EPERM; } /* check the even bits of reg 0x40 */ @@ -1247,7 +1240,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]", reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); - goto error; + return -EPERM; } /* check dsp_fw_base_addr */ @@ -1256,7 +1249,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]", reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); - goto error; + return -EPERM; } /* check dsp_cfg_base_addr */ @@ -1265,15 +1258,10 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); - goto error; + return -EPERM; } - mutex_unlock(&aw_dev->dsp_lock); return 0; - -error: - mutex_unlock(&aw_dev->dsp_lock); - return -EPERM; } int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c index 5c7982891def..809c2faa0c0c 100644 --- a/sound/soc/codecs/aw88399-lib.c +++ b/sound/soc/codecs/aw88399-lib.c @@ -1282,7 +1282,7 @@ static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg int aw88399_request_firmware_file(struct aw88399 *aw88399) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; int ret; aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; @@ -1298,13 +1298,11 @@ int aw88399_request_firmware_file(struct aw88399 *aw88399) aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw88399->aw_cfg) { - release_firmware(cont); + if (!aw88399->aw_cfg) return -ENOMEM; - } + aw88399->aw_cfg->len = (int)cont->size; memcpy(aw88399->aw_cfg->data, cont->data, cont->size); - release_firmware(cont); ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); if (ret) { diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index 13495e71ad0e..e9dab5e7bae3 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -7,6 +7,7 @@ // Author: Weidong Wang <wangweidong.a@awinic.com> // +#include <linux/cleanup.h> #include <linux/crc32.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -497,11 +498,10 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); int ret; - mutex_lock(&aw88399->lock); + guard(mutex)(&aw88399->lock); ret = aw88399_dev_set_profile_index(aw88399->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88399->lock); return 0; } @@ -510,8 +510,6 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, aw88399_start(aw88399, AW88399_SYNC_START); } - mutex_unlock(&aw88399->lock); - return 1; } @@ -690,7 +688,7 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88399->lock); + guard(mutex)(&aw88399->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88399_start(aw88399, AW88399_ASYNC_START); @@ -701,7 +699,6 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88399->lock); return 0; } diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index c0bf45b76cb8..b085885e3f18 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -85,13 +85,9 @@ static int bt_sco_probe(struct platform_device *pdev) } static const struct platform_device_id bt_sco_driver_ids[] = { - { - .name = "dfbmcs320", - }, - { - .name = "bt-sco", - }, - {}, + { .name = "dfbmcs320" }, + { .name = "bt-sco" }, + { } }; MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c index 6b80c455b074..fc41e1547bda 100644 --- a/sound/soc/codecs/cpcap.c +++ b/sound/soc/codecs/cpcap.c @@ -26,14 +26,6 @@ /* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */ #define CPCAP_BIT_PTT_S 11 /* Push To Talk */ -/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ -#define CPCAP_BIT_AUDIO_LOW_PWR 6 -#define CPCAP_BIT_AUD_LOWPWR_SPEED 5 -#define CPCAP_BIT_VAUDIOPRISTBY 4 -#define CPCAP_BIT_VAUDIO_MODE1 2 -#define CPCAP_BIT_VAUDIO_MODE0 1 -#define CPCAP_BIT_V_AUDIO_EN 0 - /* Register 513 CPCAP_REG_CC --- CODEC */ #define CPCAP_BIT_CDC_CLK2 15 #define CPCAP_BIT_CDC_CLK1 14 @@ -239,7 +231,6 @@ struct cpcap_reg_info { }; static const struct cpcap_reg_info cpcap_default_regs[] = { - { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 }, { CPCAP_REG_CC, 0xFFFF, 0x0000 }, { CPCAP_REG_CC, 0xFFFF, 0x0000 }, { CPCAP_REG_CDI, 0xBFFF, 0x0000 }, @@ -1265,6 +1256,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_component *component = dai->component; struct device *dev = component->dev; struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); @@ -1298,7 +1290,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, return err; } - return 0; + return snd_soc_runtime_set_dai_fmt(rtd, rtd->dai_link->dai_fmt); } static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, @@ -1391,121 +1383,8 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } - -/* - * Configure codec for voice call if requested. - * - * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt() - * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the - * cpcap related hardware as CPU is not involved in the voice call. - */ -static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai, - bool voice_call) -{ - int mask, err; - - /* Modem to codec VAUDIO_MODE1 */ - mask = BIT(CPCAP_BIT_VAUDIO_MODE1); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_VAUDIOC, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Clear MIC1_MUX for call */ - mask = BIT(CPCAP_BIT_MIC1_MUX); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, - mask, voice_call ? 0 : mask); - if (err) - return err; - - /* Set MIC2_MUX for call */ - mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) | - BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Enable LDSP for call */ - mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Enable CPCAP_BIT_PGA_CDC_EN for call */ - mask = BIT(CPCAP_BIT_PGA_CDC_EN); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Unmute voice for call */ - if (dai) { - err = snd_soc_dai_digital_mute(dai, !voice_call, - SNDRV_PCM_STREAM_PLAYBACK); - if (err) - return err; - } - - /* Set modem to codec mic CDC and HPF for call */ - mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) | - BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) | - BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CC, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Enable modem to codec CDC for call*/ - mask = BIT(CPCAP_BIT_CDC_CLK_EN); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, - mask, voice_call ? mask : 0); - - return err; -} - -static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, unsigned int rx_mask, - int slots, int slot_width) -{ - struct snd_soc_component *component = dai->component; - struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); - int err, ts_mask, mask; - bool voice_call; - - /* - * Primitive test for voice call, probably needs more checks - * later on for 16-bit calls detected, Bluetooth headset etc. - */ - if (tx_mask == 0 && rx_mask == 1 && slot_width == 8) - voice_call = true; - else - voice_call = false; - - ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0; - ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0; - - mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0; - mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0; - - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, - ts_mask, mask); - if (err) - return err; - - err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, slot_width * 1000); - if (err) - return err; - - err = cpcap_voice_call(cpcap, dai, voice_call); - if (err) - return err; - - return 0; -} - -static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction) +static int cpcap_voice_set_mute(struct snd_soc_dai *dai, + int mute, int direction) { struct snd_soc_component *component = dai->component; struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); @@ -1526,7 +1405,6 @@ static const struct snd_soc_dai_ops cpcap_dai_voice_ops = { .hw_params = cpcap_voice_hw_params, .set_sysclk = cpcap_voice_set_dai_sysclk, .set_fmt = cpcap_voice_set_dai_fmt, - .set_tdm_slot = cpcap_voice_set_tdm_slot, .mute_stream = cpcap_voice_set_mute, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 7dc5a7c3ca96..1148e37e29ce 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -12,6 +12,7 @@ #include <linux/acpi.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/cleanup.h> #include <linux/io.h> #include <linux/jiffies.h> #include <linux/kernel.h> @@ -319,10 +320,18 @@ static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) (uint8_t *)&p, sizeof(p), NULL, 0); } +static const u64 i2s_rx_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops i2s_rx_dai_ops = { .hw_params = i2s_rx_hw_params, .set_fmt = i2s_rx_set_fmt, .set_bclk_ratio = i2s_rx_set_bclk_ratio, + .auto_selectable_formats = &i2s_rx_selectable_formats, + .num_auto_selectable_formats = 1, }; static int i2s_rx_event(struct snd_soc_dapm_widget *w, @@ -608,10 +617,10 @@ static void wov_copy_work(struct work_struct *w) container_of(w, struct cros_ec_codec_priv, wov_copy_work.work); int ret; - mutex_lock(&priv->wov_dma_lock); + guard(mutex)(&priv->wov_dma_lock); if (!priv->wov_substream) { dev_warn(priv->dev, "no pcm substream\n"); - goto leave; + return; } if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) @@ -624,8 +633,6 @@ static void wov_copy_work(struct work_struct *w) msecs_to_jiffies(10)); else if (ret) dev_err(priv->dev, "failed to read audio data\n"); -leave: - mutex_unlock(&priv->wov_dma_lock); } static int wov_enable_get(struct snd_kcontrol *kcontrol, @@ -895,12 +902,11 @@ static int wov_pcm_hw_params(struct snd_soc_component *component, struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - mutex_lock(&priv->wov_dma_lock); + guard(mutex)(&priv->wov_dma_lock); priv->wov_substream = substream; priv->wov_rp = priv->wov_wp = 0; priv->wov_dma_offset = 0; priv->wov_burst_read = true; - mutex_unlock(&priv->wov_dma_lock); return 0; } @@ -911,10 +917,10 @@ static int wov_pcm_hw_free(struct snd_soc_component *component, struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - mutex_lock(&priv->wov_dma_lock); - wov_queue_dequeue(priv, wov_queue_size(priv)); - priv->wov_substream = NULL; - mutex_unlock(&priv->wov_dma_lock); + scoped_guard(mutex, &priv->wov_dma_lock) { + wov_queue_dequeue(priv, wov_queue_size(priv)); + priv->wov_substream = NULL; + } cancel_delayed_work_sync(&priv->wov_copy_work); diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 371e99205b58..41a9a5b005c6 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -7,6 +7,7 @@ #include <asm/byteorder.h> #include <kunit/static_stub.h> +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/dev_printk.h> #include <linux/efi.h> @@ -83,10 +84,12 @@ static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { - mutex_lock(&dsp->pwr_lock); - cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); - ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); - mutex_unlock(&dsp->pwr_lock); + scoped_guard(mutex, &dsp->pwr_lock) { + cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, + controls->mem_region, + controls->alg_id); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); + } if (ret < 0) { dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index c835088de578..8288fb6d50be 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -538,7 +538,15 @@ static int cs35l32_runtime_resume(struct device *dev) gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); regcache_cache_only(cs35l32->regmap, false); - regcache_sync(cs35l32->regmap); + ret = regcache_sync(cs35l32->regmap); + if (ret) { + regcache_cache_only(cs35l32->regmap, true); + regcache_mark_dirty(cs35l32->regmap); + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + return ret; + } return 0; } diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index f49edb9ea1f2..e217da124549 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -40,6 +40,7 @@ struct cs35l33_private { struct regmap *regmap; struct gpio_desc *reset_gpio; bool amp_cal; + bool irq_requested; int mclk_int; struct regulator_bulk_data core_supplies[2]; int num_core_supplies; @@ -881,6 +882,9 @@ static int cs35l33_runtime_resume(struct device *dev) goto err; } + if (cs35l33->irq_requested) + enable_irq(to_i2c_client(dev)->irq); + return 0; err: @@ -900,6 +904,10 @@ static int cs35l33_runtime_suspend(struct device *dev) /* redo the calibration in next power up */ cs35l33->amp_cal = false; + /* Drain and block the threaded IRQ before cache_only/power-off. */ + if (cs35l33->irq_requested) + disable_irq(to_i2c_client(dev)->irq); + regcache_cache_only(cs35l33->regmap, true); regcache_mark_dirty(cs35l33->regmap); regulator_bulk_disable(cs35l33->num_core_supplies, @@ -1154,10 +1162,12 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client) } ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, - cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "cs35l33", cs35l33); + cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs35l33", cs35l33); if (ret != 0) dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); + else + cs35l33->irq_requested = true; /* We could issue !RST or skip it based on AMP topology */ cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index e80984b22159..3f265bd760f7 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -45,6 +45,7 @@ struct cs35l34_private { int num_core_supplies; int mclk_int; bool tdm_mode; + bool irq_requested; struct gpio_desc *reset_gpio; /* Active-low reset GPIO */ }; @@ -1032,10 +1033,12 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client) } ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, - cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "cs35l34", cs35l34); + cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs35l34", cs35l34); if (ret != 0) dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); + else + cs35l34->irq_requested = true; cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset", GPIOD_OUT_LOW); @@ -1140,6 +1143,9 @@ static int cs35l34_runtime_resume(struct device *dev) dev_err(dev, "Failed to restore register cache\n"); goto err; } + + if (cs35l34->irq_requested) + enable_irq(to_i2c_client(dev)->irq); return 0; err: regcache_cache_only(cs35l34->regmap, true); @@ -1153,6 +1159,10 @@ static int cs35l34_runtime_suspend(struct device *dev) { struct cs35l34_private *cs35l34 = dev_get_drvdata(dev); + /* Drain and block the threaded IRQ before cache_only/power-off. */ + if (cs35l34->irq_requested) + disable_irq(to_i2c_client(dev)->irq); + regcache_cache_only(cs35l34->regmap, true); regcache_mark_dirty(cs35l34->regmap); diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 4f6ddf1c5a3f..5e69ddbe342a 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -51,15 +51,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client) return dev_err_probe(cs35l56->base.dev, ret, "Failed to allocate register map\n"); } - ret = cs35l56_common_probe(cs35l56); - if (ret != 0) - return ret; - - ret = cs35l56_irq_request(&cs35l56->base, client->irq); - if (ret < 0) - cs35l56_remove(cs35l56); - - return ret; + return cs35l56_common_probe(cs35l56, client->irq); } static void cs35l56_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 0a55b93b96f9..4fba59e80c37 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -231,7 +231,7 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) * a soft reset. */ if (cs35l56->base.init_done) - cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); + cs35l56_unmask_soundwire_interrupts(cs35l56); out: pm_runtime_put_autosuspend(cs35l56->base.dev); @@ -240,47 +240,17 @@ out: static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, struct sdw_slave_intr_status *status) { - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - - /* SoundWire core holds our pm_runtime when calling this function. */ - - dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port); - - if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0) - return 0; - /* - * Prevent bus manager suspending and possibly issuing a - * bus-reset before the queued work has run. + * The IRQ itself was handled through the regmap_irq handler, this is + * just clearing up the additional Cirrus SoundWire registers that are + * not covered by the SoundWire framework or the IRQ handler itself. */ - pm_runtime_get_noresume(cs35l56->base.dev); - - /* - * Mask and clear until it has been handled. - * None of the interrupts are time-critical so use the - * power-efficient queue. - */ - cs35l56_mask_soundwire_interrupts(peripheral); - queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); return 0; } -static void cs35l56_sdw_irq_work(struct work_struct *work) -{ - struct cs35l56_private *cs35l56 = container_of(work, - struct cs35l56_private, - sdw_irq_work); - - cs35l56_irq(-1, &cs35l56->base); - - /* unmask interrupts */ - if (!cs35l56->sdw_irq_no_unmask) - cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); - - pm_runtime_put_autosuspend(cs35l56->base.dev); -} - static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); @@ -302,6 +272,7 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); prop->paging_support = true; + prop->use_domain_irq = true; prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; @@ -406,7 +377,7 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) if (ret) return ret; - cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); + cs35l56_unmask_soundwire_interrupts(cs35l56); return 0; } @@ -418,21 +389,12 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) if (!cs35l56->base.init_done) return 0; - cs35l56_disable_sdw_interrupts(cs35l56); + /* runtime_resume unmasks the interrupt */ + cs35l56_mask_soundwire_interrupts(cs35l56); return cs35l56_system_suspend(dev); } -static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev) -{ - struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); - - cs35l56->sdw_irq_no_unmask = false; - /* runtime_resume re-enables the interrupt */ - - return cs35l56_system_resume(dev); -} - static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) { struct device *dev = &peripheral->dev; @@ -447,7 +409,6 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi cs35l56->base.dev = dev; cs35l56->sdw_peripheral = peripheral; cs35l56->sdw_link_num = peripheral->bus->link_id; - INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); dev_set_drvdata(dev, cs35l56); @@ -457,6 +418,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi regmap_config = &cs35l56_regmap_sdw; break; case 0x3563: + case 0x3562: regmap_config = &cs35l63_regmap_sdw; break; default: @@ -483,25 +445,21 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi /* Start in cache-only until device is enumerated */ regcache_cache_only(cs35l56->base.regmap, true); - ret = cs35l56_common_probe(cs35l56); - if (ret != 0) - return ret; - - return 0; + return cs35l56_common_probe(cs35l56, peripheral->irq); } static void cs35l56_sdw_remove(struct sdw_slave *peripheral) { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - cs35l56_disable_sdw_interrupts(cs35l56); + cs35l56_mask_soundwire_interrupts(cs35l56); cs35l56_remove(cs35l56); } static const struct dev_pm_ops cs35l56_sdw_pm = { SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume) + SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ }; @@ -509,6 +467,7 @@ static const struct dev_pm_ops cs35l56_sdw_pm = { static const struct sdw_device_id cs35l56_sdw_id[] = { SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556), SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557), + SDW_SLAVE_ENTRY(0x01FA, 0x3562, 0x3562), SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563), {}, }; diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index f14e2eaaa4ee..7b3e37d462d6 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -96,6 +96,7 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) ARRAY_SIZE(cs35l56_patch_fw)); break; case 0x63: + case 0x62: ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw, ARRAY_SIZE(cs35l63_patch_fw)); break; @@ -389,6 +390,7 @@ static void cs35l56_set_fw_reg_table(struct cs35l56_base *cs35l56_base) } break; case 0x63: + case 0x62: cs35l56_base->fw_reg = &cs35l63_fw_reg; break; } @@ -595,6 +597,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) } break; case 0x63: + case 0x62: regmap_multi_reg_write_bypassed(cs35l56_base->regmap, cs35l63_system_reset_seq, ARRAY_SIZE(cs35l63_system_reset_seq)); @@ -613,26 +616,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) } EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, "SND_SOC_CS35L56_SHARED"); -int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) -{ - int ret; - - if (irq < 1) - return 0; - - ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, - IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, - "cs35l56", cs35l56_base); - if (!ret) - cs35l56_base->irq = irq; - else - dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); - -irqreturn_t cs35l56_irq(int irq, void *data) +static irqreturn_t cs35l56_irq(int irq, void *data) { struct cs35l56_base *cs35l56_base = data; unsigned int status1 = 0, status8 = 0, status20 = 0; @@ -640,23 +624,22 @@ irqreturn_t cs35l56_irq(int irq, void *data) unsigned int val; int rv; - irqreturn_t ret = IRQ_NONE; - if (!cs35l56_base->init_done) return IRQ_NONE; - mutex_lock(&cs35l56_base->irq_lock); + guard(mutex)(&cs35l56_base->irq_lock); - rv = pm_runtime_resume_and_get(cs35l56_base->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED(cs35l56_base->dev, pm); + rv = PM_RUNTIME_ACQUIRE_ERR(&pm); if (rv < 0) { dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv); - goto err_unlock; + return IRQ_NONE; } regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val); if ((val & CS35L56_IRQ1_STS_MASK) == 0) { dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n"); - goto err; + return IRQ_NONE; } /* Ack interrupts */ @@ -680,7 +663,7 @@ irqreturn_t cs35l56_irq(int irq, void *data) /* Check to see if unmasked bits are active */ if (!status1 && !status8 && !status20) - goto err; + return IRQ_NONE; if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) dev_crit(cs35l56_base->dev, "Amp short error\n"); @@ -688,16 +671,27 @@ irqreturn_t cs35l56_irq(int irq, void *data) if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) dev_crit(cs35l56_base->dev, "Overtemp error\n"); - ret = IRQ_HANDLED; + return IRQ_HANDLED; +} -err: - pm_runtime_put(cs35l56_base->dev); -err_unlock: - mutex_unlock(&cs35l56_base->irq_lock); +int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) +{ + int ret; + + if (irq < 1) + return 0; + + ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "cs35l56", cs35l56_base); + if (!ret) + cs35l56_base->irq = irq; + else + dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); return ret; } -EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED"); +EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) { @@ -816,14 +810,10 @@ int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_sou if (!cs35l56_base->init_done) return 0; - if (!cs35l56_base->can_hibernate) - goto out_sync; - - /* Must be done before releasing cache-only */ - if (!is_soundwire) + /* Hibernate wake must be done before releasing cache-only */ + if (cs35l56_base->can_hibernate && !is_soundwire) cs35l56_issue_wake_event(cs35l56_base); -out_sync: ret = cs35l56_wait_for_firmware_boot(cs35l56_base); if (ret) { dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); @@ -1470,6 +1460,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) cs35l56_base->calibration_controls = &cs35l56_calibration_controls; break; case 0x35A630: + case 0x35A620: cs35l56_base->calibration_controls = &cs35l63_calibration_controls; devid = devid >> 4; break; diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c index b1eb924a5b6c..21b18da9e73d 100644 --- a/sound/soc/codecs/cs35l56-spi.c +++ b/sound/soc/codecs/cs35l56-spi.c @@ -40,15 +40,7 @@ static int cs35l56_spi_probe(struct spi_device *spi) if (ret) return ret; - ret = cs35l56_common_probe(cs35l56); - if (ret != 0) - return ret; - - ret = cs35l56_irq_request(&cs35l56->base, spi->irq); - if (ret < 0) - cs35l56_remove(cs35l56); - - return ret; + return cs35l56_common_probe(cs35l56, spi->irq); } static void cs35l56_spi_remove(struct spi_device *spi) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 063ef7a70de0..b9118ad8fab5 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -37,48 +37,49 @@ #include "wm_adsp.h" #include "cs35l56.h" -void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral) +void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56) { /* + * Mask unconditionally. + * * The read of GEN_INT_STAT_1 is required as per the SoundWire spec * for interrupt status bits to clear. * GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. */ - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); - sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); } EXPORT_SYMBOL_NS_GPL(cs35l56_mask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); -void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral) +void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56) { - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ); + if (!cs35l56->base.irq) + return; + + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); } EXPORT_SYMBOL_NS_GPL(cs35l56_unmask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); -void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) +static void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) { if (!cs35l56->sdw_peripheral) return; - cs35l56->sdw_irq_no_unmask = true; - flush_work(&cs35l56->sdw_irq_work); - - /* Mask interrupts and flush in case sdw_irq_work was queued again */ - cs35l56_mask_soundwire_interrupts(cs35l56->sdw_peripheral); - flush_work(&cs35l56->sdw_irq_work); + cs35l56_mask_soundwire_interrupts(cs35l56); + if (cs35l56->base.irq) + disable_irq(cs35l56->base.irq); } -EXPORT_SYMBOL_NS_GPL(cs35l56_disable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); -void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) +static void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) { - if (!cs35l56->sdw_peripheral) + if (!cs35l56->sdw_peripheral || !cs35l56->base.irq) return; - cs35l56->sdw_irq_no_unmask = false; - cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); + enable_irq(cs35l56->base.irq); + cs35l56_unmask_soundwire_interrupts(cs35l56); } -EXPORT_SYMBOL_NS_GPL(cs35l56_enable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -828,11 +829,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing { int ret; - /* - * Disable SoundWire interrupts to prevent race with IRQ work. - * Setting sdw_irq_no_unmask prevents the handler re-enabling - * the SoundWire interrupt. - */ + /* Disable SoundWire interrupts to prevent race with IRQ handler thread */ cs35l56_disable_sdw_interrupts(cs35l56); ret = cs35l56_firmware_shutdown(&cs35l56->base); @@ -1402,6 +1399,7 @@ static int _cs35l56_component_probe(struct snd_soc_component *component) ARRAY_SIZE(cs35l56_controls)); break; case 0x63: + case 0x62: ret = snd_soc_add_component_controls(component, cs35l63_controls, ARRAY_SIZE(cs35l63_controls)); break; @@ -1941,7 +1939,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 return ret; } -int cs35l56_common_probe(struct cs35l56_private *cs35l56) +int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq) { int ret; @@ -2018,16 +2016,24 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) goto err_remove_wm_adsp; } + ret = cs35l56_irq_request(&cs35l56->base, irq); + if (ret) + goto err_remove_wm_adsp; + ret = snd_soc_register_component(cs35l56->base.dev, &soc_component_dev_cs35l56, cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) { dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); - goto err_remove_wm_adsp; + goto err_free_irq; } return 0; +err_free_irq: + if (cs35l56->base.irq) + devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base); + err_remove_wm_adsp: wm_adsp2_remove(&cs35l56->dsp); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 9acd2e7e17c9..35c02ae17de3 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -39,8 +39,6 @@ struct cs35l56_private { struct sdw_slave *sdw_peripheral; struct regmap *sdw_bus_regmap; const char *fallback_fw_suffix; - struct work_struct sdw_irq_work; - bool sdw_irq_no_unmask; bool soft_resetting; bool sdw_attached; struct completion init_completion; @@ -65,10 +63,8 @@ static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_b extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; -void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral); -void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral); -void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56); -void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56); +void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56); +void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56); int cs35l56_system_suspend(struct device *dev); int cs35l56_system_suspend_late(struct device *dev); @@ -76,9 +72,8 @@ int cs35l56_system_suspend_no_irq(struct device *dev); int cs35l56_system_resume_no_irq(struct device *dev); int cs35l56_system_resume_early(struct device *dev); int cs35l56_system_resume(struct device *dev); -irqreturn_t cs35l56_irq(int irq, void *data); int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); -int cs35l56_common_probe(struct cs35l56_private *cs35l56); +int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq); int cs35l56_init(struct cs35l56_private *cs35l56); void cs35l56_remove(struct cs35l56_private *cs35l56); diff --git a/sound/soc/codecs/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c index aa629ef53db4..676367163067 100644 --- a/sound/soc/codecs/cs40l50-codec.c +++ b/sound/soc/codecs/cs40l50-codec.c @@ -288,8 +288,8 @@ static int cs40l50_codec_driver_probe(struct platform_device *pdev) } static const struct platform_device_id cs40l50_id[] = { - { "cs40l50-codec", }, - {} + { .name = "cs40l50-codec" }, + { } }; MODULE_DEVICE_TABLE(platform, cs40l50_id); diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c index ad1256910a18..b61ca74dcf0b 100644 --- a/sound/soc/codecs/cs42l42-sdw.c +++ b/sound/soc/codecs/cs42l42-sdw.c @@ -481,10 +481,20 @@ static int cs42l42_sdw_runtime_resume(struct device *dev) regcache_cache_only(cs42l42->regmap, false); /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ - regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); - regcache_sync(cs42l42->regmap); + ret = regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); + if (ret) + goto err_sync; + + ret = regcache_sync(cs42l42->regmap); + if (ret) + goto err_sync; return 0; + +err_sync: + regcache_cache_only(cs42l42->regmap, true); + regcache_mark_dirty(cs42l42->regmap); + return ret; } static int cs42l42_sdw_resume(struct device *dev) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 2652a639a79a..fadf68860601 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -9,6 +9,7 @@ * Author: Michael White <michael.white@cirrus.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -565,7 +566,7 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); /* Prevent race with interrupt handler */ - mutex_lock(&cs42l42->irq_lock); + guard(mutex)(&cs42l42->irq_lock); cs42l42->jack = jk; if (jk) { @@ -581,7 +582,6 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ break; } } - mutex_unlock(&cs42l42->irq_lock); return 0; } @@ -1668,13 +1668,10 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) unsigned int current_button_status; unsigned int i; - pm_runtime_get_sync(cs42l42->dev); - mutex_lock(&cs42l42->irq_lock); - if (cs42l42->suspended || !cs42l42->init_done) { - mutex_unlock(&cs42l42->irq_lock); - pm_runtime_put_autosuspend(cs42l42->dev); + guard(pm_runtime_active_auto)(cs42l42->dev); + guard(mutex)(&cs42l42->irq_lock); + if (cs42l42->suspended || !cs42l42->init_done) return IRQ_NONE; - } /* Read sticky registers to clear interurpt */ for (i = 0; i < ARRAY_SIZE(stickies); i++) { @@ -1774,9 +1771,6 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) } } - mutex_unlock(&cs42l42->irq_lock); - pm_runtime_put_autosuspend(cs42l42->dev); - return IRQ_HANDLED; } EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, "SND_SOC_CS42L42_CORE"); @@ -2163,22 +2157,22 @@ int cs42l42_suspend(struct device *dev) * future interrupts. This ensures a safe disable if the interrupt * is shared. */ - mutex_lock(&cs42l42->irq_lock); - cs42l42->suspended = true; + scoped_guard(mutex, &cs42l42->irq_lock) { + cs42l42->suspended = true; - /* Save register values that will be overwritten by shutdown sequence */ - for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { - regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); - save_regs[i] = (u8)reg; - } + /* Save register values that will be overwritten by shutdown sequence */ + for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { + regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); + save_regs[i] = (u8)reg; + } - /* Shutdown codec */ - regmap_multi_reg_write(cs42l42->regmap, - cs42l42_shutdown_seq, - ARRAY_SIZE(cs42l42_shutdown_seq)); + /* Shutdown codec */ + regmap_multi_reg_write(cs42l42->regmap, + cs42l42_shutdown_seq, + ARRAY_SIZE(cs42l42_shutdown_seq)); - /* All interrupt sources are now disabled */ - mutex_unlock(&cs42l42->irq_lock); + /* All interrupt sources are now disabled */ + } /* Wait for power-down complete */ msleep(CS42L42_PDN_DONE_TIME_MS); @@ -2250,13 +2244,13 @@ void cs42l42_resume_restore(struct device *dev) regcache_cache_only(cs42l42->regmap, false); regcache_mark_dirty(cs42l42->regmap); - mutex_lock(&cs42l42->irq_lock); - /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ - regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); - regcache_sync(cs42l42->regmap); + scoped_guard(mutex, &cs42l42->irq_lock) { + /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ + regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); + regcache_sync(cs42l42->regmap); - cs42l42->suspended = false; - mutex_unlock(&cs42l42->irq_lock); + cs42l42->suspended = false; + } dev_dbg(dev, "System resumed\n"); } diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index dbf45cabfffe..a2555dd9229e 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. #include <linux/build_bug.h> +#include <linux/cleanup.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/errno.h> @@ -67,6 +68,21 @@ static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const pro return -EINVAL; } +static void cs42l43_apply_accdet_config(struct cs42l43_codec *priv, + unsigned int autocontrol, + unsigned int pdncntl) +{ + struct cs42l43 *cs42l43 = priv->core; + + regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | + CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); + regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, + CS42L43_RING_SENSE_EN_MASK, pdncntl); + + dev_dbg(priv->dev, "Successfully configured accessory detect\n"); +} + int cs42l43_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *d) { @@ -80,31 +96,34 @@ int cs42l43_set_jack(struct snd_soc_component *component, dev_dbg(priv->dev, "Configure accessory detect\n"); - ret = pm_runtime_resume_and_get(priv->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) { dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret); return ret; } - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); priv->jack_hp = jack; - if (!jack) - goto done; + if (!jack) { + cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); + return 0; + } ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms"); if (ret != -EINVAL) { if (ret < 0) { dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n", ret); - goto error; + return ret; } if (ret > CS42L43_N_BUTTONS) { ret = -EINVAL; dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n"); - goto error; + return ret; } ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", @@ -112,7 +131,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, if (ret < 0) { dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n", ret); - goto error; + return ret; } } else { priv->buttons[0] = 70; @@ -124,7 +143,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, ret = cs42l43_find_index(priv, "cirrus,detect-us", 50000, &priv->detect_us, cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); if (ret < 0) - goto error; + return ret; hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; @@ -134,7 +153,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms, ARRAY_SIZE(cs42l43_accdet_ramp_ms)); if (ret < 0) - goto error; + return ret; hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; @@ -142,7 +161,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->bias_sense_ua, cs42l43_accdet_bias_sense, ARRAY_SIZE(cs42l43_accdet_bias_sense)); if (ret < 0) - goto error; + return ret; if (priv->bias_sense_ua) autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; @@ -154,7 +173,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->tip_debounce_ms); if (ret < 0 && ret != -EINVAL) { dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret); - goto error; + return ret; } /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ @@ -170,7 +189,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->tip_fall_db_ms, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) - goto error; + return ret; tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; @@ -178,7 +197,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->tip_rise_db_ms, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) - goto error; + return ret; tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; @@ -199,7 +218,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, NULL, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) - goto error; + return ret; ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; @@ -207,7 +226,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, NULL, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) - goto error; + return ret; ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; pdncntl |= CS42L43_RING_SENSE_EN_MASK; @@ -228,23 +247,9 @@ int cs42l43_set_jack(struct snd_soc_component *component, CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | CS42L43_AUTO_HSDET_TIME_MASK, hs2); -done: - ret = 0; - - regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, - CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | - CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); - regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, - CS42L43_RING_SENSE_EN_MASK, pdncntl); - - dev_dbg(priv->dev, "Successfully configured accessory detect\n"); - -error: - mutex_unlock(&priv->jack_lock); - - pm_runtime_put_autosuspend(priv->dev); + cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); - return ret; + return 0; } static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect) @@ -370,22 +375,22 @@ irqreturn_t cs42l43_button_press(int irq, void *data) { struct cs42l43_codec *priv = data; struct cs42l43 *cs42l43 = priv->core; - irqreturn_t iret = IRQ_NONE; unsigned int buttons = 0; unsigned int val = 0; int i, ret; - ret = pm_runtime_resume_and_get(priv->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) { dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); - return iret; + return IRQ_NONE; } - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); if (!priv->button_detect_running) { dev_dbg(priv->dev, "Spurious button press IRQ\n"); - goto error; + return IRQ_NONE; } // Wait for 2 full cycles of comb filter to ensure good reading @@ -396,12 +401,12 @@ irqreturn_t cs42l43_button_press(int irq, void *data) /* Bail if jack removed, the button is irrelevant and likely invalid */ if (!cs42l43_jack_present(priv)) { dev_dbg(priv->dev, "Button ignored due to removal\n"); - goto error; + return IRQ_NONE; } if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { dev_dbg(priv->dev, "Button ignored due to bias sense\n"); - goto error; + return IRQ_NONE; } val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; @@ -424,45 +429,32 @@ irqreturn_t cs42l43_button_press(int irq, void *data) snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); - iret = IRQ_HANDLED; - -error: - mutex_unlock(&priv->jack_lock); - - pm_runtime_put_autosuspend(priv->dev); - - return iret; + return IRQ_HANDLED; } irqreturn_t cs42l43_button_release(int irq, void *data) { struct cs42l43_codec *priv = data; - irqreturn_t iret = IRQ_NONE; int ret; - ret = pm_runtime_resume_and_get(priv->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) { dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); - return iret; + return IRQ_NONE; } - mutex_lock(&priv->jack_lock); - - if (priv->button_detect_running) { - dev_dbg(priv->dev, "Button release IRQ\n"); - - snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); + guard(mutex)(&priv->jack_lock); - iret = IRQ_HANDLED; - } else { + if (!priv->button_detect_running) { dev_dbg(priv->dev, "Spurious button release IRQ\n"); + return IRQ_NONE; } - mutex_unlock(&priv->jack_lock); + dev_dbg(priv->dev, "Button release IRQ\n"); + snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); - pm_runtime_put_autosuspend(priv->dev); - - return iret; + return IRQ_HANDLED; } void cs42l43_bias_sense_timeout(struct work_struct *work) @@ -472,13 +464,14 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) struct cs42l43 *cs42l43 = priv->core; int ret; - ret = pm_runtime_resume_and_get(priv->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) { dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret); return; } - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); if (cs42l43_jack_present(priv) && priv->button_detect_running) { dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n"); @@ -491,10 +484,6 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); } - - mutex_unlock(&priv->jack_lock); - - pm_runtime_put_autosuspend(priv->dev); } static const struct reg_sequence cs42l43_3pole_patch[] = { @@ -896,9 +885,8 @@ int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); ucontrol->value.integer.value[0] = priv->jack_override; - mutex_unlock(&priv->jack_lock); return 0; } @@ -914,17 +902,13 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u if (override >= e->items) return -EINVAL; - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); - if (!cs42l43_jack_present(priv)) { - mutex_unlock(&priv->jack_lock); + if (!cs42l43_jack_present(priv)) return -EBUSY; - } - if (override == priv->jack_override) { - mutex_unlock(&priv->jack_lock); + if (override == priv->jack_override) return 0; - } priv->jack_override = override; @@ -984,7 +968,5 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u cs42l43_jack_override_modes[override].report); } - mutex_unlock(&priv->jack_lock); - return 1; } diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 1d133577702e..1b817e1a6ea5 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -8,6 +8,7 @@ #include <linux/bitops.h> #include <linux/bits.h> #include <linux/build_bug.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/err.h> @@ -1207,14 +1208,12 @@ static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv) { struct cs42l43 *cs42l43 = priv->core; - mutex_lock(&priv->spk_vu_lock); + guard(mutex)(&priv->spk_vu_lock); regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK); regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, CS42L43_AMP1_2_VU_MASK, 0); - - mutex_unlock(&priv->spk_vu_lock); } static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) @@ -1601,7 +1600,7 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, struct cs42l43 *cs42l43 = priv->core; int ret; - mutex_lock(&cs42l43->pll_lock); + guard(mutex)(&cs42l43->pll_lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1626,8 +1625,6 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, break; } - mutex_unlock(&cs42l43->pll_lock); - return ret; } @@ -2565,13 +2562,10 @@ static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, { struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); struct cs42l43 *cs42l43 = priv->core; - int ret; - mutex_lock(&cs42l43->pll_lock); - ret = cs42l43_set_pll(priv, src, freq); - mutex_unlock(&cs42l43->pll_lock); + guard(mutex)(&cs42l43->pll_lock); - return ret; + return cs42l43_set_pll(priv, src, freq); } static int cs42l43_component_probe(struct snd_soc_component *component) @@ -2941,8 +2935,8 @@ static const struct dev_pm_ops cs42l43_codec_pm_ops = { }; static const struct platform_device_id cs42l43_codec_id_table[] = { - { "cs42l43-codec", }, - {} + { .name = "cs42l43-codec" }, + { } }; MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index f2a58163de0e..36533a13319d 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -10,6 +10,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -281,10 +282,9 @@ static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); /* Prevent race with interrupt handler */ - mutex_lock(&cs42l84->irq_lock); + guard(mutex)(&cs42l84->irq_lock); cs42l84->jack = jk; snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET); - mutex_unlock(&cs42l84->irq_lock); return 0; } @@ -831,7 +831,7 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) u8 current_ring_state; int i; - mutex_lock(&cs42l84->irq_lock); + guard(mutex)(&cs42l84->irq_lock); /* Read sticky registers to clear interrupt */ for (i = 0; i < ARRAY_SIZE(stickies); i++) { regmap_read(cs42l84->regmap, irq_params_table[i].status_addr, @@ -902,8 +902,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) break; } - mutex_unlock(&cs42l84->irq_lock); - return IRQ_HANDLED; } @@ -919,8 +917,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) } } - mutex_unlock(&cs42l84->irq_lock); - return IRQ_HANDLED; } diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index e7b06f962790..48d88dad4ceb 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -6,6 +6,7 @@ * * Authors: Li Xu <li.xu@cirrus.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -818,26 +819,26 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream, unsigned int required_clk; u8 dsd_speed; - mutex_lock(&cs43130->clk_mutex); - if (!cs43130->clk_req) { - /* no DAI is currently using clk */ - if (!(CS43130_MCLK_22M % params_rate(params))) - required_clk = CS43130_MCLK_22M; - else - required_clk = CS43130_MCLK_24M; + scoped_guard(mutex, &cs43130->clk_mutex) { + if (!cs43130->clk_req) { + /* no DAI is currently using clk */ + if (!(CS43130_MCLK_22M % params_rate(params))) + required_clk = CS43130_MCLK_22M; + else + required_clk = CS43130_MCLK_24M; + + cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); + if (cs43130->pll_bypass) + cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); + else + cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + } - cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); - if (cs43130->pll_bypass) - cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); - else - cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + cs43130->clk_req++; + if (cs43130->clk_req == 2) + cs43130_pcm_dsd_mix(true, cs43130->regmap); } - cs43130->clk_req++; - if (cs43130->clk_req == 2) - cs43130_pcm_dsd_mix(true, cs43130->regmap); - mutex_unlock(&cs43130->clk_mutex); - switch (params_rate(params)) { case 176400: dsd_speed = 0; @@ -881,26 +882,26 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, unsigned int required_clk; u8 dsd_speed; - mutex_lock(&cs43130->clk_mutex); - if (!cs43130->clk_req) { - /* no DAI is currently using clk */ - if (!(CS43130_MCLK_22M % params_rate(params))) - required_clk = CS43130_MCLK_22M; - else - required_clk = CS43130_MCLK_24M; + scoped_guard(mutex, &cs43130->clk_mutex) { + if (!cs43130->clk_req) { + /* no DAI is currently using clk */ + if (!(CS43130_MCLK_22M % params_rate(params))) + required_clk = CS43130_MCLK_22M; + else + required_clk = CS43130_MCLK_24M; + + cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); + if (cs43130->pll_bypass) + cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); + else + cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + } - cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); - if (cs43130->pll_bypass) - cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); - else - cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + cs43130->clk_req++; + if (cs43130->clk_req == 2) + cs43130_pcm_dsd_mix(true, cs43130->regmap); } - cs43130->clk_req++; - if (cs43130->clk_req == 2) - cs43130_pcm_dsd_mix(true, cs43130->regmap); - mutex_unlock(&cs43130->clk_mutex); - switch (dai->id) { case CS43130_ASP_DOP_DAI: case CS43130_XSP_DOP_DAI: @@ -988,14 +989,13 @@ static int cs43130_hw_free(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); - mutex_lock(&cs43130->clk_mutex); + guard(mutex)(&cs43130->clk_mutex); cs43130->clk_req--; if (!cs43130->clk_req) { /* no DAI is currently using clk */ cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO); cs43130_pcm_dsd_mix(false, cs43130->regmap); } - mutex_unlock(&cs43130->clk_mutex); return 0; } diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index 6ac6d306b054..9a7f70175717 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -340,7 +340,13 @@ static int cs4349_runtime_resume(struct device *dev) gpiod_set_value_cansleep(cs4349->reset_gpio, 1); regcache_cache_only(cs4349->regmap, false); - regcache_sync(cs4349->regmap); + ret = regcache_sync(cs4349->regmap); + if (ret) { + regcache_cache_only(cs4349->regmap, true); + regcache_mark_dirty(cs4349->regmap); + gpiod_set_value_cansleep(cs4349->reset_gpio, 0); + return ret; + } return 0; } diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c index da64e0a1db28..2f30dd834709 100644 --- a/sound/soc/codecs/cs47l15.c +++ b/sound/soc/codecs/cs47l15.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> @@ -1285,9 +1286,8 @@ static int cs47l15_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -1317,9 +1317,8 @@ static void cs47l15_component_remove(struct snd_soc_component *component) struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); struct madera *madera = cs47l15->core.madera; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; wm_adsp2_component_remove(&cs47l15->core.adsp[0], component); } diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c index a8fe5a99a8bb..e77ac7bc8466 100644 --- a/sound/soc/codecs/cs47l35.c +++ b/sound/soc/codecs/cs47l35.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/init.h> @@ -1566,9 +1567,8 @@ static int cs47l35_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -1600,9 +1600,8 @@ static void cs47l35_component_remove(struct snd_soc_component *component) struct madera *madera = cs47l35->core.madera; int i; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; for (i = 0; i < CS47L35_NUM_ADSP; i++) wm_adsp2_component_remove(&cs47l35->core.adsp[i], component); diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c index 42fafb0b392c..9ee60616c04c 100644 --- a/sound/soc/codecs/cs47l85.c +++ b/sound/soc/codecs/cs47l85.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> @@ -2504,9 +2505,8 @@ static int cs47l85_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -2537,9 +2537,8 @@ static void cs47l85_component_remove(struct snd_soc_component *component) struct madera *madera = cs47l85->core.madera; int i; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; for (i = 0; i < CS47L85_NUM_ADSP; i++) wm_adsp2_component_remove(&cs47l85->core.adsp[i], component); diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c index 77e8aabb241a..6ce603bfd500 100644 --- a/sound/soc/codecs/cs47l90.c +++ b/sound/soc/codecs/cs47l90.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> @@ -2423,9 +2424,8 @@ static int cs47l90_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -2456,9 +2456,8 @@ static void cs47l90_component_remove(struct snd_soc_component *component) struct madera *madera = cs47l90->core.madera; int i; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; for (i = 0; i < CS47L90_NUM_ADSP; i++) wm_adsp2_component_remove(&cs47l90->core.adsp[i], component); diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c index 868237bd6d91..07bc303d9797 100644 --- a/sound/soc/codecs/cs47l92.c +++ b/sound/soc/codecs/cs47l92.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> @@ -1892,9 +1893,8 @@ static int cs47l92_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -1922,9 +1922,8 @@ static void cs47l92_component_remove(struct snd_soc_component *component) struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); struct madera *madera = cs47l92->core.madera; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; wm_adsp2_component_remove(&cs47l92->core.adsp[0], component); } diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c index 086ed0f57a85..a9967771124e 100644 --- a/sound/soc/codecs/cs48l32.c +++ b/sound/soc/codecs/cs48l32.c @@ -8,6 +8,7 @@ #include <dt-bindings/sound/cs48l32.h> #include <linux/array_size.h> #include <linux/build_bug.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/container_of.h> #include <linux/delay.h> @@ -236,15 +237,13 @@ static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v int ret; /* Prevent any mixer mux changes while we do this */ - mutex_lock(&cs48l32_codec->rate_lock); + guard(mutex)(&cs48l32_codec->rate_lock); /* The write must be guarded by a number of SYSCLK cycles */ cs48l32_spin_sysclk(cs48l32_codec); ret = snd_soc_put_enum_double(kcontrol, ucontrol); cs48l32_spin_sysclk(cs48l32_codec); - mutex_unlock(&cs48l32_codec->rate_lock); - return ret; } @@ -2242,7 +2241,6 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1]; unsigned int base = dai->driver->base; unsigned int current_asp_rate, target_asp_rate; - bool change_rate_domain = false; int ret; if (clk_id == dai_priv->clk) @@ -2284,19 +2282,15 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, if ((current_asp_rate & CS48L32_ASP_RATE_MASK) != (target_asp_rate & CS48L32_ASP_RATE_MASK)) { - change_rate_domain = true; - - mutex_lock(&cs48l32_codec->rate_lock); /* Guard the rate change with SYSCLK cycles */ - cs48l32_spin_sysclk(cs48l32_codec); - } - - snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1, - CS48L32_ASP_RATE_MASK, target_asp_rate); - - if (change_rate_domain) { - cs48l32_spin_sysclk(cs48l32_codec); - mutex_unlock(&cs48l32_codec->rate_lock); + scoped_guard(mutex, &cs48l32_codec->rate_lock) { + cs48l32_spin_sysclk(cs48l32_codec); + snd_soc_component_update_bits(component, + base + CS48L32_ASP_CONTROL1, + CS48L32_ASP_RATE_MASK, + target_asp_rate); + cs48l32_spin_sysclk(cs48l32_codec); + } } } diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 83c6cbd40804..3d92c10e8500 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -9,6 +9,7 @@ // #include <linux/acpi.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/gpio.h> @@ -96,51 +97,60 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_GPIO_WAKE, 0x00000000 }, { CX2072X_GPIO_UM_ENABLE, 0x00000000 }, { CX2072X_GPIO_STICKY_MASK, 0x00000000 }, - { CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 }, - { CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a }, - { CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a }, { CX2072X_DAC1_POWER_STATE, 0x00000433 }, { CX2072X_DAC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, { CX2072X_DAC1_EAPD_ENABLE, 0x00000000 }, - { CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 }, - { CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a }, - { CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a }, + { CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 }, { CX2072X_DAC2_POWER_STATE, 0x00000433 }, { CX2072X_DAC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, - { CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a }, + { CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0x00000000 }, + { CX2072X_ADC1_POWER_STATE, 0x00000433 }, + { CX2072X_ADC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, { CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_3, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_4, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_5, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_6, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_LEFT_6, 0x0000004a }, - { CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0x00000000 }, - { CX2072X_ADC1_POWER_STATE, 0x00000433 }, - { CX2072X_ADC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, - { CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0x00000000 }, + { CX2072X_ADC2_POWER_STATE, 0x00000433 }, + { CX2072X_ADC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, { CX2072X_ADC2_AMP_GAIN_RIGHT_0, 0x0000004a }, - { CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a }, { CX2072X_ADC2_AMP_GAIN_RIGHT_1, 0x0000004a }, - { CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a }, { CX2072X_ADC2_AMP_GAIN_RIGHT_2, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a }, { CX2072X_ADC2_AMP_GAIN_LEFT_2, 0x0000004a }, - { CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0x00000000 }, - { CX2072X_ADC2_POWER_STATE, 0x00000433 }, - { CX2072X_ADC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, + { CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_MIXER_POWER_STATE, 0x00000433 }, + { CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a }, + { CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a }, + { CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a }, { CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0x00000000 }, { CX2072X_PORTA_POWER_STATE, 0x00000433 }, { CX2072X_PORTA_PIN_CTRL, 0x000000c0 }, { CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x00000000 }, { CX2072X_PORTA_PIN_SENSE, 0x00000000 }, { CX2072X_PORTA_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTG_POWER_STATE, 0x00000433 }, + { CX2072X_PORTG_PIN_CTRL, 0x00000040 }, + { CX2072X_PORTG_EAPD_BTL, 0x00000002 }, { CX2072X_PORTB_POWER_STATE, 0x00000433 }, { CX2072X_PORTB_PIN_CTRL, 0x00000000 }, { CX2072X_PORTB_UNSOLICITED_RESPONSE, 0x00000000 }, @@ -148,43 +158,16 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_PORTB_EAPD_BTL, 0x00000002 }, { CX2072X_PORTB_GAIN_RIGHT, 0x00000000 }, { CX2072X_PORTB_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTC_POWER_STATE, 0x00000433 }, - { CX2072X_PORTC_PIN_CTRL, 0x00000000 }, - { CX2072X_PORTC_GAIN_RIGHT, 0x00000000 }, - { CX2072X_PORTC_GAIN_LEFT, 0x00000000 }, { CX2072X_PORTD_POWER_STATE, 0x00000433 }, { CX2072X_PORTD_PIN_CTRL, 0x00000020 }, { CX2072X_PORTD_UNSOLICITED_RESPONSE, 0x00000000 }, { CX2072X_PORTD_PIN_SENSE, 0x00000000 }, { CX2072X_PORTD_GAIN_RIGHT, 0x00000000 }, { CX2072X_PORTD_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 }, - { CX2072X_PORTE_POWER_STATE, 0x00000433 }, - { CX2072X_PORTE_PIN_CTRL, 0x00000040 }, - { CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 }, - { CX2072X_PORTE_PIN_SENSE, 0x00000000 }, - { CX2072X_PORTE_EAPD_BTL, 0x00000002 }, - { CX2072X_PORTE_GAIN_RIGHT, 0x00000000 }, - { CX2072X_PORTE_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTF_POWER_STATE, 0x00000433 }, - { CX2072X_PORTF_PIN_CTRL, 0x00000000 }, - { CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 }, - { CX2072X_PORTF_PIN_SENSE, 0x00000000 }, - { CX2072X_PORTF_GAIN_RIGHT, 0x00000000 }, - { CX2072X_PORTF_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTG_POWER_STATE, 0x00000433 }, - { CX2072X_PORTG_PIN_CTRL, 0x00000040 }, - { CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 }, - { CX2072X_PORTG_EAPD_BTL, 0x00000002 }, - { CX2072X_PORTM_POWER_STATE, 0x00000433 }, - { CX2072X_PORTM_PIN_CTRL, 0x00000000 }, - { CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 }, - { CX2072X_PORTM_EAPD_BTL, 0x00000002 }, - { CX2072X_MIXER_POWER_STATE, 0x00000433 }, - { CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a }, - { CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a }, - { CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a }, - { CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_PORTC_POWER_STATE, 0x00000433 }, + { CX2072X_PORTC_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTC_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTC_GAIN_LEFT, 0x00000000 }, { CX2072X_SPKR_DRC_ENABLE_STEP, 0x040065a4 }, { CX2072X_SPKR_DRC_CONTROL, 0x007b0024 }, { CX2072X_SPKR_DRC_TEST, 0x00000000 }, @@ -195,12 +178,15 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_I2SPCM_CONTROL3, 0x00000000 }, { CX2072X_I2SPCM_CONTROL4, 0x00000000 }, { CX2072X_I2SPCM_CONTROL5, 0x00000000 }, - { CX2072X_I2SPCM_CONTROL6, 0x00000000 }, { CX2072X_UM_INTERRUPT_CRTL_E, 0x00000000 }, + { CX2072X_I2SPCM_CONTROL6, 0x00000000 }, + { CX2072X_DIGITAL_TEST16, 0x00000021 }, + { CX2072X_DIGITAL_TEST17, 0x00000018 }, + { CX2072X_DIGITAL_TEST18, 0x00000024 }, + { CX2072X_DIGITAL_TEST19, 0x00000001 }, + { CX2072X_DIGITAL_TEST20, 0x00000002 }, { CX2072X_CODEC_TEST2, 0x00000000 }, { CX2072X_CODEC_TEST9, 0x00000004 }, - { CX2072X_CODEC_TEST20, 0x00000600 }, - { CX2072X_CODEC_TEST26, 0x00000208 }, { CX2072X_ANALOG_TEST4, 0x00000000 }, { CX2072X_ANALOG_TEST5, 0x00000000 }, { CX2072X_ANALOG_TEST6, 0x0000059a }, @@ -215,11 +201,26 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_DIGITAL_TEST11, 0x00000000 }, { CX2072X_DIGITAL_TEST12, 0x00000084 }, { CX2072X_DIGITAL_TEST15, 0x00000077 }, - { CX2072X_DIGITAL_TEST16, 0x00000021 }, - { CX2072X_DIGITAL_TEST17, 0x00000018 }, - { CX2072X_DIGITAL_TEST18, 0x00000024 }, - { CX2072X_DIGITAL_TEST19, 0x00000001 }, - { CX2072X_DIGITAL_TEST20, 0x00000002 }, + { CX2072X_CODEC_TEST20, 0x00000600 }, + { CX2072X_CODEC_TEST26, 0x00000208 }, + { CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTE_POWER_STATE, 0x00000433 }, + { CX2072X_PORTE_PIN_CTRL, 0x00000040 }, + { CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTE_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTE_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTE_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTE_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTF_POWER_STATE, 0x00000433 }, + { CX2072X_PORTF_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTF_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTF_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTF_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTM_POWER_STATE, 0x00000433 }, + { CX2072X_PORTM_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTM_EAPD_BTL, 0x00000002 }, }; /* @@ -1408,7 +1409,7 @@ static int cx2072x_jack_status_check(void *data) unsigned int type = 0; int state = 0; - mutex_lock(&cx2072x->lock); + guard(mutex)(&cx2072x->lock); regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack); jack = jack >> 24; @@ -1434,8 +1435,6 @@ static int cx2072x_jack_status_check(void *data) /* clear interrupt */ regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24); - mutex_unlock(&cx2072x->lock); - dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n", type, state); return state; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 4bf91ab2553a..923b997efbc4 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -11,6 +11,7 @@ #include <linux/acpi.h> #include <linux/of.h> #include <linux/property.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -216,13 +217,10 @@ static int da7213_volsw_locked_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7213->ctrl_lock); - ret = snd_soc_get_volsw(kcontrol, ucontrol); - mutex_unlock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); - return ret; + return snd_soc_get_volsw(kcontrol, ucontrol); } static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, @@ -230,13 +228,10 @@ static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7213->ctrl_lock); - ret = snd_soc_put_volsw(kcontrol, ucontrol); - mutex_unlock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); - return ret; + return snd_soc_put_volsw(kcontrol, ucontrol); } static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, @@ -244,13 +239,10 @@ static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7213->ctrl_lock); - ret = snd_soc_get_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); - return ret; + return snd_soc_get_enum_double(kcontrol, ucontrol); } static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, @@ -258,13 +250,10 @@ static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7213->ctrl_lock); - ret = snd_soc_put_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); - return ret; + return snd_soc_put_enum_double(kcontrol, ucontrol); } /* ALC */ @@ -465,9 +454,8 @@ static int da7213_tonegen_freq_get(struct snd_kcontrol *kcontrol, __le16 val; int ret; - mutex_lock(&da7213->ctrl_lock); - ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); - mutex_unlock(&da7213->ctrl_lock); + scoped_guard(mutex, &da7213->ctrl_lock) + ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); if (ret) return ret; @@ -499,12 +487,11 @@ static int da7213_tonegen_freq_put(struct snd_kcontrol *kcontrol, */ val_new = cpu_to_le16(ucontrol->value.integer.value[0]); - mutex_lock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); ret = regmap_raw_read(da7213->regmap, reg, &val_old, sizeof(val_old)); if (ret == 0 && (val_old != val_new)) ret = regmap_raw_write(da7213->regmap, reg, &val_new, sizeof(val_new)); - mutex_unlock(&da7213->ctrl_lock); if (ret < 0) return ret; diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index f0874d891e12..f6d371e93549 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -8,6 +8,7 @@ */ #include <linux/acpi.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clkdev.h> #include <linux/clk-provider.h> @@ -256,13 +257,10 @@ static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->ctrl_lock); - ret = snd_soc_get_volsw(kcontrol, ucontrol); - mutex_unlock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); - return ret; + return snd_soc_get_volsw(kcontrol, ucontrol); } static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, @@ -270,13 +268,10 @@ static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->ctrl_lock); - ret = snd_soc_put_volsw(kcontrol, ucontrol); - mutex_unlock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); - return ret; + return snd_soc_put_volsw(kcontrol, ucontrol); } static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, @@ -284,13 +279,10 @@ static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->ctrl_lock); - ret = snd_soc_get_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); - return ret; + return snd_soc_get_enum_double(kcontrol, ucontrol); } static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, @@ -298,13 +290,10 @@ static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->ctrl_lock); - ret = snd_soc_put_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); - return ret; + return snd_soc_put_enum_double(kcontrol, ucontrol); } /* ALC */ @@ -422,9 +411,8 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, __le16 val; int ret; - mutex_lock(&da7219->ctrl_lock); - ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); - mutex_unlock(&da7219->ctrl_lock); + scoped_guard(mutex, &da7219->ctrl_lock) + ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); if (ret) return ret; @@ -456,12 +444,11 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, */ val_new = cpu_to_le16(ucontrol->value.integer.value[0]); - mutex_lock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); ret = regmap_raw_read(da7219->regmap, reg, &val_old, sizeof(val_old)); if (ret == 0 && (val_old != val_new)) ret = regmap_raw_write(da7219->regmap, reg, - &val_new, sizeof(val_new)); - mutex_unlock(&da7219->ctrl_lock); + &val_new, sizeof(val_new)); if (ret < 0) return ret; @@ -1167,15 +1154,12 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); int ret = 0; - mutex_lock(&da7219->pll_lock); + guard(mutex)(&da7219->pll_lock); - if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) { - mutex_unlock(&da7219->pll_lock); + if (da7219->clk_src == clk_id && da7219->mclk_rate == freq) return 0; - } - if ((freq < 2000000) || (freq > 54000000)) { - mutex_unlock(&da7219->pll_lock); + if (freq < 2000000 || freq > 54000000) { dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", freq); return -EINVAL; @@ -1193,7 +1177,6 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, break; default: dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); - mutex_unlock(&da7219->pll_lock); return -EINVAL; } @@ -1203,17 +1186,13 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, freq = clk_round_rate(da7219->mclk, freq); ret = clk_set_rate(da7219->mclk, freq); if (ret) { - dev_err(codec_dai->dev, "Failed to set clock rate %d\n", - freq); - mutex_unlock(&da7219->pll_lock); + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", freq); return ret; } } da7219->mclk_rate = freq; - mutex_unlock(&da7219->pll_lock); - return 0; } @@ -1296,13 +1275,10 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, { struct snd_soc_component *component = codec_dai->component; struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->pll_lock); - ret = da7219_set_pll(component, source, fout); - mutex_unlock(&da7219->pll_lock); + guard(mutex)(&da7219->pll_lock); - return ret; + return da7219_set_pll(component, source, fout); } static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index 3abe77423f29..24ec8b211cdb 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -9,11 +9,13 @@ #include <linux/module.h> #include <linux/acpi.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -30,6 +32,13 @@ static const unsigned int supported_mclk_lrck_ratios[] = { 256, 384, 400, 500, 512, 768, 1024 }; +static const char * const es8316_supply_names[] = { + "avdd", + "cpvdd", + "dvdd", + "pvdd", +}; + struct es8316_priv { struct mutex lock; struct clk *mclk; @@ -620,15 +629,15 @@ static irqreturn_t es8316_irq(int irq, void *data) struct snd_soc_component *comp = es8316->component; unsigned int flags; - mutex_lock(&es8316->lock); + guard(mutex)(&es8316->lock); regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); if (flags == 0x00) - goto out; /* Powered-down / reset */ + return IRQ_HANDLED; /* Powered-down / reset */ /* Catch spurious IRQ before set_jack is called */ if (!es8316->jack) - goto out; + return IRQ_HANDLED; if (es8316->jd_inverted) flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; @@ -681,8 +690,6 @@ static irqreturn_t es8316_irq(int irq, void *data) } } -out: - mutex_unlock(&es8316->lock); return IRQ_HANDLED; } @@ -699,18 +706,16 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component, es8316->jd_inverted = device_property_read_bool(component->dev, "everest,jack-detect-inverted"); - mutex_lock(&es8316->lock); - - es8316->jack = jack; + scoped_guard(mutex, &es8316->lock) { + es8316->jack = jack; - if (es8316->jack->status & SND_JACK_MICROPHONE) - es8316_enable_micbias_for_mic_gnd_short_detect(component); - - snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, - ES8316_GPIO_ENABLE_INTERRUPT, - ES8316_GPIO_ENABLE_INTERRUPT); + if (es8316->jack->status & SND_JACK_MICROPHONE) + es8316_enable_micbias_for_mic_gnd_short_detect(component); - mutex_unlock(&es8316->lock); + snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, + ES8316_GPIO_ENABLE_INTERRUPT, + ES8316_GPIO_ENABLE_INTERRUPT); + } /* Enable irq and sync initial jack state */ enable_irq(es8316->irq); @@ -726,7 +731,7 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) disable_irq(es8316->irq); - mutex_lock(&es8316->lock); + guard(mutex)(&es8316->lock); snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, ES8316_GPIO_ENABLE_INTERRUPT, 0); @@ -737,8 +742,6 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) } es8316->jack = NULL; - - mutex_unlock(&es8316->lock); } static int es8316_set_jack(struct snd_soc_component *component, @@ -871,6 +874,11 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client) i2c_set_clientdata(i2c_client, es8316); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(es8316_supply_names), + es8316_supply_names); + if (ret) + return dev_err_probe(dev, ret, "unable to enable supplies\n"); + es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); if (IS_ERR(es8316->regmap)) return PTR_ERR(es8316->regmap); diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index a79b2da35099..c5460589a88b 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -6,6 +6,7 @@ // Authors: David Yang <yangxiaohua@everest-semi.com> // +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/i2c.h> #include <linux/interrupt.h> @@ -790,7 +791,7 @@ static void es8326_jack_button_handler(struct work_struct *work) if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */ return; - mutex_lock(&es8326->lock); + guard(mutex)(&es8326->lock); iface = snd_soc_component_read(comp, ES8326_HPDET_STA); switch (iface) { case 0x93: @@ -845,7 +846,6 @@ static void es8326_jack_button_handler(struct work_struct *work) } es8326_disable_micbias(es8326->component); } - mutex_unlock(&es8326->lock); } static void es8326_jack_detect_handler(struct work_struct *work) @@ -855,7 +855,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) struct snd_soc_component *comp = es8326->component; unsigned int iface; - mutex_lock(&es8326->lock); + guard(mutex)(&es8326->lock); iface = snd_soc_component_read(comp, ES8326_HPDET_STA); dev_dbg(comp->dev, "gpio flag %#04x", iface); @@ -873,7 +873,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? ~es8326->jack_pol : es8326->jack_pol)); - goto exit; + return; } if ((iface & ES8326_HPINSERT_FLAG) == 0) { @@ -930,7 +930,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) queue_delayed_work(system_dfl_wq, &es8326->jack_detect_work, msecs_to_jiffies(400)); es8326->hp = 1; - goto exit; + return; } if (es8326->jack->status & SND_JACK_HEADSET) { /* detect button */ @@ -939,7 +939,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); es8326_enable_micbias(es8326->component); queue_delayed_work(system_dfl_wq, &es8326->button_press_work, 10); - goto exit; + return; } if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) { dev_dbg(comp->dev, "Headphone detected\n"); @@ -958,8 +958,6 @@ static void es8326_jack_detect_handler(struct work_struct *work) usleep_range(10000, 15000); } } -exit: - mutex_unlock(&es8326->lock); } static irqreturn_t es8326_irq(int irq, void *dev_id) @@ -1200,13 +1198,12 @@ static void es8326_enable_jack_detect(struct snd_soc_component *component, { struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); - mutex_lock(&es8326->lock); - if (es8326->jd_inverted) - snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, - ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); - es8326->jack = jack; - - mutex_unlock(&es8326->lock); + scoped_guard(mutex, &es8326->lock) { + if (es8326->jd_inverted) + snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); + es8326->jack = jack; + } es8326_irq(es8326->irq, es8326); } @@ -1219,13 +1216,12 @@ static void es8326_disable_jack_detect(struct snd_soc_component *component) return; /* Already disabled (or never enabled) */ cancel_delayed_work_sync(&es8326->jack_detect_work); - mutex_lock(&es8326->lock); + guard(mutex)(&es8326->lock); if (es8326->jack->status & SND_JACK_MICROPHONE) { es8326_disable_micbias(component); snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); } es8326->jack = NULL; - mutex_unlock(&es8326->lock); } static int es8326_set_jack(struct snd_soc_component *component, diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 9838fe42cb6f..aaa6646ad4c5 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -405,6 +405,11 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = { { "Mic Bias", NULL, "Mic Bias Gen" }, + { "LINPUT1", NULL, "Mic Bias" }, + { "RINPUT1", NULL, "Mic Bias" }, + { "LINPUT2", NULL, "Mic Bias" }, + { "RINPUT2", NULL, "Mic Bias" }, + { "Left Mixer", NULL, "Left DAC" }, { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, { "Left Mixer", "Right Playback Switch", "Right DAC" }, diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 3484c87853cb..80efce3e0a22 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -36,6 +36,10 @@ struct es8389_private { unsigned int sysclk; int mastermode; + u8 hpfl; + u8 hpfr; + u32 hpf_freq; + u32 capture_rate; u8 mclk_src; u8 vddd; int version; @@ -50,10 +54,29 @@ static const char * const es8389_core_supplies[] = { static bool es8389_volatile_register(struct device *dev, unsigned int reg) { - if ((reg <= 0xff)) - return true; - else + switch (reg) { + case ES8389_ADCL_VOL: + case ES8389_ADCR_VOL: + case ES8389_MIC1_GAIN: + case ES8389_MIC2_GAIN: + case ES8389_DACL_VOL: + case ES8389_DACR_VOL: + case ES8389_ALC_ON: + case ES8389_ALC_CTL: + case ES8389_ALC_TARGET: + case ES8389_ALC_GAIN: + case ES8389_ADC_MUTE: + case ES8389_OSR_VOL: + case ES8389_DAC_INV: + case ES8389_MIX_VOL: + case ES8389_DAC_MIX: + case ES8389_ADC_RESET: + case ES8389_ADC_MODE: + case ES8389_DMIC_EN: return false; + default: + return true; + } } static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0); @@ -63,6 +86,92 @@ static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0); static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0); static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0); +static const u32 hpf_table[10][10] = { + {1020, 754, 624, 559, 527, 511, 502, 498, 497, 496}, + {754, 495, 368, 306, 274, 259, 251, 247, 246, 244}, + {624, 368, 243, 182, 151, 136, 128, 124, 123, 121}, + {559, 306, 182, 120, 90, 75, 68, 63, 62, 60}, + {527, 274, 151, 90, 60, 45, 38, 33, 32, 31}, + {511, 259, 136, 75, 45, 30, 23, 19, 18, 17}, + {502, 251, 128, 68, 38, 23, 16, 13, 11, 11}, + {498, 247, 124, 63, 33, 19, 13, 10, 8, 8}, + {497, 246, 123, 62, 32, 18, 11, 8, 8, 0}, + {496, 244, 121, 60, 31, 17, 11, 8, 0, 0} +}; + +static bool find_best_hpf_freq(u32 target_hz, u8 *hpf1, u8 *hpf2, u32 *out) +{ + int best_row = -1, best_col = -1; + u32 min_diff = U32_MAX; + u32 f, diff; + int i, j; + + if (target_hz > 1020) + return false; + + for (i = 0; i < 10; i++) { + for (j = i; j < 10; j++) { + f = hpf_table[i][j]; + + diff = (target_hz > f) ? (target_hz - f) : (f - target_hz); + if (diff < min_diff) { + min_diff = diff; + best_row = i; + best_col = j; + *out = f; + } + } + } + + *hpf1 = best_col + ES8389_HPF_OFFSET; + *hpf2 = best_row + ES8389_HPF_OFFSET; + + return true; +} + +static int es8389_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8389->hpf_freq; + return 0; +} + +static int es8389_hpf_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + u32 freq; + bool hpf; + + if (es8389->hpf_freq == ucontrol->value.integer.value[0]) + return 0; + + if (es8389->capture_rate) { + freq = (ucontrol->value.integer.value[0] * 48000) / es8389->capture_rate; + + hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); + if (!hpf) + return -EBUSY; + + if (es8389->hpf_freq != ucontrol->value.integer.value[0]) + dev_dbg(component->dev, "At the %u Hz sampling rate, %ld Hz could not be obtained." + "the frequency has been set to the closest value, %u Hz\n", + es8389->capture_rate, ucontrol->value.integer.value[0], es8389->hpf_freq); + + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); + } else { + es8389->hpf_freq = ucontrol->value.integer.value[0]; + dev_dbg(component->dev, "PCM_STREAM_CAPTURE is not active.retain the input frequency\n"); + } + + return 1; +} + static int es8389_dmic_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -143,6 +252,16 @@ static const struct soc_enum alc_ramprate = static const struct soc_enum alc_winsize = SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize); +static const char *const es8389_adcl_mux_txt[] = { + "Normal", + "ADC2 channel to ADC1 channel", +}; + +static const char *const es8389_adcr_mux_txt[] = { + "Normal", + "ADC1 channel to ADC2 channel", +}; + static const char *const es8389_outl_mux_txt[] = { "Normal", "DAC2 channel to DAC1 channel", @@ -170,6 +289,20 @@ static const unsigned int es8389_pga_values[] = { 1, 5, 6 }; +static const struct soc_enum es8389_adcl_mux_enum = + SOC_ENUM_SINGLE(ES8389_ADC_MODE, 5, + ARRAY_SIZE(es8389_adcl_mux_txt), es8389_adcl_mux_txt); + +static const struct snd_kcontrol_new es8389_adcl_mux_controls = + SOC_DAPM_ENUM("INPUTL MUX", es8389_adcl_mux_enum); + +static const struct soc_enum es8389_adcr_mux_enum = + SOC_ENUM_SINGLE(ES8389_ADC_MODE, 4, + ARRAY_SIZE(es8389_adcr_mux_txt), es8389_adcr_mux_txt); + +static const struct snd_kcontrol_new es8389_adcr_mux_controls = + SOC_DAPM_ENUM("INPUTR MUX", es8389_adcr_mux_enum); + static const struct soc_enum es8389_outl_mux_enum = SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5, ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt); @@ -240,6 +373,8 @@ static const struct snd_kcontrol_new es8389_snd_controls[] = { SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0), SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv), SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0), + SOC_SINGLE_EXT("ADC HPF Freq Select", SND_SOC_NOPM, 0, 1020, 0, + es8389_hpf_get, es8389_hpf_set), SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv), SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv), @@ -298,6 +433,8 @@ static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = { &es8389_adc_mixer_controls[0], ARRAY_SIZE(es8389_adc_mixer_controls)), SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls), + SND_SOC_DAPM_MUX("INPUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_adcl_mux_controls), + SND_SOC_DAPM_MUX("INPUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_adcr_mux_controls), SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls), SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls), @@ -311,10 +448,15 @@ static const struct snd_soc_dapm_route es8389_dapm_routes[] = { {"ADCL", NULL, "PGAL"}, {"ADCR", NULL, "PGAR"}, + {"INPUTL MUX", "Normal", "ADCL"}, + {"INPUTL MUX", "ADC2 channel to ADC1 channel", "ADCR"}, + {"INPUTR MUX", "Normal", "ADCR"}, + {"INPUTR MUX", "ADC1 channel to ADC2 channel", "ADCL"}, + {"ADC Mixer", "DACL ADCL Mixer", "DACL"}, {"ADC Mixer", "DACR ADCR Mixer", "DACR"}, - {"ADC Mixer", NULL, "ADCL"}, - {"ADC Mixer", NULL, "ADCR"}, + {"ADC Mixer", NULL, "INPUTL MUX"}, + {"ADC Mixer", NULL, "INPUTR MUX"}, {"ADC MUX", "AMIC", "ADC Mixer"}, {"ADC MUX", "DMIC", "DMIC"}, @@ -415,52 +557,54 @@ static const struct _coeff_div coeff_div[] = { {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {150, 2400000, 16000, 0x02, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14, 2, 2}, {375, 12000000, 32000, 0x0E, 0x2E, 0x05, 0xC8, 0x00, 0xC2, 0x80, 0x40, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 0}, - {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, - {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, + {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25, 2, 2}, - {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, + {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, {96, 9216000, 96000, 0x02, 0x43, 0x00, 0xC0, 0x10, 0xC0, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xBF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 2, 2}, + {64, 12288000, 192000, 0x00, 0x41, 0x00, 0xC0, 0x18, 0xC1, 0x80, 0x00, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 1, 0}, }; static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) @@ -564,6 +708,8 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, int coeff, ret; u8 dmic_enable, state = 0; unsigned int regv; + u32 freq; + bool hpf; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -590,7 +736,7 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, if (es8389->mclk_src == ES8389_SCLK_PIN) { regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK, - ES8389_MCLK_SOURCE, es8389->mclk_src); + ES8389_MCLK_MASK, ES8389_MCLK_FROM_SCLK); es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params); } @@ -641,6 +787,28 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, dev_warn(component->dev, "Clock coefficients do not match"); } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + es8389->capture_rate = params_rate(params); + freq = (es8389->hpf_freq * 48000) / params_rate(params); + hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); + if (!hpf) { + dev_err(component->dev, "The HPF frequency is invalid\n"); + return -EINVAL; + } + } + + return 0; +} + +static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + es8389->capture_rate = 0; + return 0; } @@ -721,8 +889,8 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, 0x03, 0x00); } else { - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a); - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, 0x03, 0x00); } @@ -738,6 +906,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) static const struct snd_soc_dai_ops es8389_ops = { .hw_params = es8389_pcm_hw_params, + .hw_free = es8389_pcm_hw_free, .set_fmt = es8389_set_dai_fmt, .set_sysclk = es8389_set_dai_sysclk, .set_tdm_slot = es8389_set_tdm_slot, @@ -771,7 +940,7 @@ static void es8389_init(struct snd_soc_component *component) regmap_read(es8389->regmap, ES8389_MAX_REGISTER, ®); es8389->version = reg; - regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00); + regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x56); regmap_write(es8389->regmap, ES8389_RESET, 0x7E); regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38); regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64); @@ -823,7 +992,7 @@ static void es8389_init(struct snd_soc_component *component) regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04); regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01); regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00); - regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00); + regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x10); regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F); regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F); regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0); @@ -861,13 +1030,13 @@ static int es8389_resume(struct snd_soc_component *component) regcache_cache_only(es8389->regmap, false); regcache_cache_bypass(es8389->regmap, true); regmap_read(es8389->regmap, ES8389_RESET, ®v); - regcache_cache_bypass(es8389->regmap, false); if (regv == 0xff) es8389_init(component); else es8389_set_bias_level(component, SND_SOC_BIAS_ON); + regcache_cache_bypass(es8389->regmap, false); regcache_sync(es8389->regmap); return 0; @@ -913,6 +1082,7 @@ static int es8389_probe(struct snd_soc_component *component) return ret; } + es8389->hpf_freq = ES8389_HPF_DEFAULT; es8389_init(component); es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h index d21e72f876a6..de39e9bf96e9 100644 --- a/sound/soc/codecs/es8389.h +++ b/sound/soc/codecs/es8389.h @@ -106,6 +106,9 @@ #define ES8389_MIC_SEL_MASK (7 << 4) #define ES8389_MIC_DEFAULT (1 << 4) +#define ES8389_HPF_DEFAULT 16 +#define ES8389_HPF_OFFSET 4 + #define ES8389_MASTER_MODE_EN (1 << 0) #define ES8389_TDM_OFF (0 << 0) @@ -116,9 +119,11 @@ #define ES8389_TDM_SLOT (0x70 << 0) #define ES8389_TDM_SHIFT 4 -#define ES8389_MCLK_SOURCE (1 << 6) -#define ES8389_MCLK_PIN (1 << 6) -#define ES8389_SCLK_PIN (0 << 6) +#define ES8389_MCLK_MASK (3 << 6) +#define ES8389_MCLK_FROM_SCLK (1 << 6) +#define ES8389_MCLK_SOURCE ES8389_MCLK_PIN +#define ES8389_MCLK_PIN 0 +#define ES8389_SCLK_PIN 1 /* ES8389_FMT */ #define ES8389_S24_LE (0 << 5) diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index 1122455aab77..80db0f2f4aef 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/module.h> #include <linux/soundwire/sdw.h> @@ -19,8 +20,6 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/tlv.h> -#include <sound/sdca_function.h> -#include <sound/sdca_regmap.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <sound/jack.h> @@ -500,16 +499,19 @@ static int es9356_power_state(struct snd_soc_dai *dai, unsigned char ps, unsigne } /* power state changes are not independent across functions */ - mutex_lock(&es9356->pde_lock); - ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps0:ps3); - if (ret) { - regmap_write(es9356->regmap, - SDW_SDCA_CTL(func, pde_entity, ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ps?ps3:ps0); - es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps3:ps0); - } else - dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ps?ps0:ps3); - - mutex_unlock(&es9356->pde_lock); + scoped_guard(mutex, &es9356->pde_lock) { + ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps0 : ps3); + if (ret) { + regmap_write(es9356->regmap, + SDW_SDCA_CTL(func, pde_entity, + ES9356_SDCA_CTL_REQ_POWER_STATE, 0), + ps ? ps3 : ps0); + es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps3 : ps0); + } else { + dev_dbg(component->dev, "%s PDE is already %d\n", __func__, + ps ? ps0 : ps3); + } + } if (rate) regmap_write(es9356->regmap, @@ -671,7 +673,7 @@ static int es9356_sdca_mbq_size(struct device *dev, unsigned int reg) } } -static struct regmap_sdw_mbq_cfg es9356_mbq_config = { +static const struct regmap_sdw_mbq_cfg es9356_mbq_config = { .mbq_size = es9356_sdca_mbq_size, }; @@ -1091,9 +1093,8 @@ static int es9356_sdca_dev_system_suspend(struct device *dev) { struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev); - mutex_lock(&es9356->disable_irq_lock); - es9356->disable_irq = true; - mutex_unlock(&es9356->disable_irq_lock); + scoped_guard(mutex, &es9356->disable_irq_lock) + es9356->disable_irq = true; return es9356_sdca_dev_suspend(dev); } diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c index c8f56617e370..a562c72fc524 100644 --- a/sound/soc/codecs/fs-amp-lib.c +++ b/sound/soc/codecs/fs-amp-lib.c @@ -221,7 +221,7 @@ static void fs_print_firmware_info(struct fs_amp_lib *amp_lib) int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) { - const struct firmware *cont; + const struct firmware *cont __free(firmware) = NULL; struct fs_fwm_header *hdr; int ret; @@ -237,7 +237,6 @@ int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size); hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL); - release_firmware(cont); if (!hdr) return -ENOMEM; diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c index d794ff918b6f..510cbeea5b62 100644 --- a/sound/soc/codecs/fs210x.c +++ b/sound/soc/codecs/fs210x.c @@ -4,6 +4,7 @@ // // Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -771,9 +772,8 @@ static int fs210x_dai_hw_params(struct snd_pcm_substream *substream, if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000) return -EOPNOTSUPP; - mutex_lock(&fs210x->lock); - ret = fs210x_set_hw_params(fs210x); - mutex_unlock(&fs210x->lock); + scoped_guard(mutex, &fs210x->lock) + ret = fs210x_set_hw_params(fs210x); if (ret) dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret); @@ -790,15 +790,11 @@ static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream) fs210x = snd_soc_component_get_drvdata(dai->component); - mutex_lock(&fs210x->lock); - - if (!fs210x->is_inited || fs210x->is_suspended) { - mutex_unlock(&fs210x->lock); - return 0; + scoped_guard(mutex, &fs210x->lock) { + if (!fs210x->is_inited || fs210x->is_suspended) + return 0; } - mutex_unlock(&fs210x->lock); - if (mute) { cancel_delayed_work_sync(&fs210x->fault_check_work); cancel_delayed_work_sync(&fs210x->start_work); @@ -817,15 +813,11 @@ static int fs210x_dai_trigger(struct snd_pcm_substream *substream, fs210x = snd_soc_component_get_drvdata(dai->component); - mutex_lock(&fs210x->lock); - - if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) { - mutex_unlock(&fs210x->lock); - return 0; + scoped_guard(mutex, &fs210x->lock) { + if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) + return 0; } - mutex_unlock(&fs210x->lock); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -853,13 +845,11 @@ static void fs210x_start_work(struct work_struct *work) fs210x = container_of(work, struct fs210x_priv, start_work.work); - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); ret = fs210x_dev_play(fs210x); if (ret) dev_err(fs210x->dev, "Failed to start playing: %d\n", ret); - - mutex_unlock(&fs210x->lock); } static void fs210x_fault_check_work(struct work_struct *work) @@ -870,15 +860,13 @@ static void fs210x_fault_check_work(struct work_struct *work) fs210x = container_of(work, struct fs210x_priv, fault_check_work.work); - mutex_lock(&fs210x->lock); + scoped_guard(mutex, &fs210x->lock) { + if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) + return; - if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) { - mutex_unlock(&fs210x->lock); - return; + ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); } - ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); - mutex_unlock(&fs210x->lock); if (ret) return; @@ -991,7 +979,7 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, if (fs210x->scene_id < 1) return -EINVAL; - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); /* * FS210x has scene(s) as below: * init scene: id = 0 @@ -1000,7 +988,6 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, */ index = fs210x->scene_id - 1; ucontrol->value.integer.value[0] = index; - mutex_unlock(&fs210x->lock); return 0; } @@ -1019,7 +1006,7 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); /* * FS210x has scene(s) as below: @@ -1029,17 +1016,14 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, */ scene_id = ucontrol->value.integer.value[0] + 1; scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */ - if (scene_id < 1 || scene_id > scene_count) { - mutex_unlock(&fs210x->lock); + if (scene_id < 1 || scene_id > scene_count) return -ERANGE; - } if (scene_id != fs210x->scene_id) is_changed = true; if (fs210x->is_suspended) { fs210x->scene_id = scene_id; - mutex_unlock(&fs210x->lock); return is_changed; } @@ -1047,8 +1031,6 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, if (ret) dev_err(fs210x->dev, "Failed to set scene: %d\n", ret); - mutex_unlock(&fs210x->lock); - if (!ret && is_changed) return 1; @@ -1062,12 +1044,10 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt); int ret = 0; - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); - if (fs210x->is_suspended) { - mutex_unlock(&fs210x->lock); + if (fs210x->is_suspended) return 0; - } switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1088,8 +1068,6 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, break; } - mutex_unlock(&fs210x->lock); - return ret; } @@ -1220,11 +1198,9 @@ static int fs210x_probe(struct snd_soc_component *cmpnt) if (ret) return ret; - mutex_lock(&fs210x->lock); - ret = fs210x_init_chip(fs210x); - mutex_unlock(&fs210x->lock); + guard(mutex)(&fs210x->lock); - return ret; + return fs210x_init_chip(fs210x); } static void fs210x_remove(struct snd_soc_component *cmpnt) @@ -1251,15 +1227,15 @@ static int fs210x_suspend(struct snd_soc_component *cmpnt) regcache_cache_only(fs210x->regmap, true); - mutex_lock(&fs210x->lock); - fs210x->cur_scene = NULL; - fs210x->is_inited = false; - fs210x->is_playing = false; - fs210x->is_suspended = true; + scoped_guard(mutex, &fs210x->lock) { + fs210x->cur_scene = NULL; + fs210x->is_inited = false; + fs210x->is_playing = false; + fs210x->is_suspended = true; - gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ - fsleep(30000); /* >= 30ms */ - mutex_unlock(&fs210x->lock); + gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ + fsleep(30000); /* >= 30ms */ + } cancel_delayed_work_sync(&fs210x->start_work); cancel_delayed_work_sync(&fs210x->fault_check_work); @@ -1288,14 +1264,11 @@ static int fs210x_resume(struct snd_soc_component *cmpnt) return ret; } - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); fs210x->is_suspended = false; - ret = fs210x_init_chip(fs210x); - mutex_unlock(&fs210x->lock); - - return ret; + return fs210x_init_chip(fs210x); } #else #define fs210x_suspend NULL diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 680e341aa7f1..1c06fdbf0e71 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -437,7 +437,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) #ifdef CONFIG_SND_HDA_PATCH_LOADER if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; dev_info(&hdev->dev, "Applying patch firmware '%s'\n", loadable_patch[hda_pvt->dev_index]); @@ -451,7 +451,6 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) dev_err(&hdev->dev, "%s: failed to load hda patch %d\n", __func__, ret); goto error_no_pm; } - release_firmware(fw); } } #endif @@ -642,10 +641,8 @@ static int hdac_hda_dev_probe(struct hdac_device *hdev) &hdac_hda_codec, hdac_hda_dais, ARRAY_SIZE(hdac_hda_dais)); - if (ret < 0) { + if (ret < 0) dev_err(&hdev->dev, "%s: failed to register HDA codec %d\n", __func__, ret); - return ret; - } snd_hdac_ext_bus_link_put(hdev->bus, hlink); diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 3220f9226e0b..d9e6fa6d3e99 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -10,6 +10,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/module.h> @@ -537,10 +538,11 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( continue; list_for_each_entry(port, &pcm->port_list, head) { - mutex_lock(&pcm->lock); - ret = hdac_hdmi_query_port_connlist(hdev, - port->pin, port); - mutex_unlock(&pcm->lock); + scoped_guard(mutex, &pcm->lock) { + ret = hdac_hdmi_query_port_connlist(hdev, + port->pin, + port); + } if (ret < 0) continue; @@ -640,11 +642,11 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); if (pcm) { - mutex_lock(&pcm->lock); - pcm->chmap_set = false; - memset(pcm->chmap, 0, sizeof(pcm->chmap)); - pcm->channels = 0; - mutex_unlock(&pcm->lock); + scoped_guard(mutex, &pcm->lock) { + pcm->chmap_set = false; + memset(pcm->chmap, 0, sizeof(pcm->chmap)); + pcm->channels = 0; + } } if (dai_map->port) @@ -922,7 +924,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, if (port == NULL) return -EINVAL; - mutex_lock(&hdmi->pin_mutex); + guard(mutex)(&hdmi->pin_mutex); list_for_each_entry(pcm, &hdmi->pcm_list, head) { if (list_empty(&pcm->port_list)) continue; @@ -945,12 +947,10 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, list_add_tail(&port->head, &pcm->port_list); if (port->eld.monitor_present && port->eld.eld_valid) { hdac_hdmi_jack_report_sync(pcm, port, true); - mutex_unlock(&hdmi->pin_mutex); return ret; } } } - mutex_unlock(&hdmi->pin_mutex); return ret; } @@ -1274,67 +1274,65 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, * In case of non MST pin, get_eld info API expectes port * to be -1. */ - mutex_lock(&hdmi->pin_mutex); - port->eld.monitor_present = false; + scoped_guard(mutex, &hdmi->pin_mutex) { + port->eld.monitor_present = false; - if (pin->mst_capable) - port_id = port->id; + if (pin->mst_capable) + port_id = port->id; - size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, - &port->eld.monitor_present, - port->eld.eld_buffer, - ELD_MAX_SIZE); + size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, + &port->eld.monitor_present, + port->eld.eld_buffer, + ELD_MAX_SIZE); - if (size > 0) { - size = min(size, ELD_MAX_SIZE); - if (hdac_hdmi_parse_eld(hdev, port) < 0) - size = -EINVAL; - } + if (size > 0) { + size = min(size, ELD_MAX_SIZE); + if (hdac_hdmi_parse_eld(hdev, port) < 0) + size = -EINVAL; + } - eld_valid = port->eld.eld_valid; + eld_valid = port->eld.eld_valid; - if (size > 0) { - port->eld.eld_valid = true; - port->eld.eld_size = size; - } else { - port->eld.eld_valid = false; - port->eld.eld_size = 0; - } + if (size > 0) { + port->eld.eld_valid = true; + port->eld.eld_size = size; + } else { + port->eld.eld_valid = false; + port->eld.eld_size = 0; + } - eld_changed = (eld_valid != port->eld.eld_valid); + eld_changed = (eld_valid != port->eld.eld_valid); - pcm = hdac_hdmi_get_pcm(hdev, port); + pcm = hdac_hdmi_get_pcm(hdev, port); - if (!port->eld.monitor_present || !port->eld.eld_valid) { + if (!port->eld.monitor_present || !port->eld.eld_valid) { - dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", - __func__, pin->nid, port->id); - - /* - * PCMs are not registered during device probe, so don't - * report jack here. It will be done in usermode mux - * control select. - */ - if (pcm) { - hdac_hdmi_jack_report(pcm, port, false); - schedule_work(&port->dapm_work); - } + dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", + __func__, pin->nid, port->id); - mutex_unlock(&hdmi->pin_mutex); - return; - } + /* + * PCMs are not registered during device probe, so don't + * report jack here. It will be done in usermode mux + * control select. + */ + if (pcm) { + hdac_hdmi_jack_report(pcm, port, false); + schedule_work(&port->dapm_work); + } - if (port->eld.monitor_present && port->eld.eld_valid) { - if (pcm) { - hdac_hdmi_jack_report(pcm, port, true); - schedule_work(&port->dapm_work); + return; } - print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, - port->eld.eld_buffer, port->eld.eld_size, false); + if (port->eld.monitor_present && port->eld.eld_valid) { + if (pcm) { + hdac_hdmi_jack_report(pcm, port, true); + schedule_work(&port->dapm_work); + } + print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, + port->eld.eld_buffer, port->eld.eld_size, false); + } } - mutex_unlock(&hdmi->pin_mutex); if (eld_changed && pcm) snd_ctl_notify(hdmi->card, @@ -1795,13 +1793,12 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, if (list_empty(&pcm->port_list)) return; - mutex_lock(&pcm->lock); + guard(mutex)(&pcm->lock); pcm->chmap_set = true; memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); list_for_each_entry(port, &pcm->port_list, head) if (prepared) hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); - mutex_unlock(&pcm->lock); } static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) @@ -1866,8 +1863,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) snd_hdac_ext_bus_link_get(hdev->bus, hlink); hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); - if (hdmi_priv == NULL) + if (hdmi_priv == NULL) { + snd_hdac_ext_bus_link_put(hdev->bus, hlink); return -ENOMEM; + } snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; @@ -1876,8 +1875,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; hdmi_priv->hdev = hdev; - if (!hdac_id) + if (!hdac_id) { + snd_hdac_ext_bus_link_put(hdev->bus, hlink); return -ENODEV; + } if (hdac_id->driver_data) hdmi_priv->drv_data = @@ -1902,6 +1903,8 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) if (ret < 0) { dev_err(&hdev->dev, "Failed in parse and map nid with err: %d\n", ret); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + snd_hdac_display_power(hdev->bus, hdev->addr, false); return ret; } snd_hdac_refresh_widgets(hdev); diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 13ae9e83bc21..bc2c22436ba6 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -4,6 +4,7 @@ * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha <jsarha@ti.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/string.h> #include <sound/core.h> @@ -452,31 +453,30 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, if (!((has_playback && tx) || (has_capture && !tx))) return 0; - mutex_lock(&hcp->lock); + guard(mutex)(&hcp->lock); if (hcp->busy) { dev_err(dai->dev, "Only one simultaneous stream supported!\n"); - mutex_unlock(&hcp->lock); return -EINVAL; } if (hcp->hcd.ops->audio_startup) { ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data); if (ret) - goto err; + return ret; } if (tx && hcp->hcd.ops->get_eld) { ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, hcp->eld, sizeof(hcp->eld)); if (ret) - goto err; + return ret; snd_parse_eld(dai->dev, &hcp->eld_parsed, hcp->eld, sizeof(hcp->eld)); ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); if (ret) - goto err; + return ret; /* Select chmap supported */ hdmi_codec_eld_chmap(hcp); @@ -484,8 +484,6 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, hcp->busy = true; -err: - mutex_unlock(&hcp->lock); return ret; } @@ -503,9 +501,8 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); - mutex_lock(&hcp->lock); + guard(mutex)(&hcp->lock); hcp->busy = false; - mutex_unlock(&hcp->lock); } static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index 084090ccef77..387f17b5f7b3 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -7,6 +7,7 @@ // Author: Herve Codina <herve.codina@bootlin.com> #include <linux/bitrev.h> +#include <linux/cleanup.h> #include <linux/gpio/driver.h> #include <linux/module.h> #include <linux/slab.h> @@ -413,12 +414,12 @@ static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol, ch = IDT821034_ID_GET_CHAN(mc->reg); - mutex_lock(&idt821034->mutex); - if (IDT821034_ID_IS_OUT(mc->reg)) - val = idt821034->amps.ch[ch].amp_out.gain; - else - val = idt821034->amps.ch[ch].amp_in.gain; - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) { + if (IDT821034_ID_IS_OUT(mc->reg)) + val = idt821034->amps.ch[ch].amp_out.gain; + else + val = idt821034->amps.ch[ch].amp_in.gain; + } ucontrol->value.integer.value[0] = val & mask; if (invert) @@ -456,7 +457,7 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, ch = IDT821034_ID_GET_CHAN(mc->reg); - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); if (IDT821034_ID_IS_OUT(mc->reg)) { amp = &idt821034->amps.ch[ch].amp_out; @@ -466,22 +467,18 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, gain_type = IDT821034_GAIN_TX; } - if (amp->gain == val) { - ret = 0; - goto end; - } + if (amp->gain == val) + return 0; if (!amp->is_muted) { ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val); if (ret) - goto end; + return ret; } amp->gain = val; - ret = 1; /* The value changed */ -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return 1; } static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, @@ -495,11 +492,11 @@ static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, ch = IDT821034_ID_GET_CHAN(id); - mutex_lock(&idt821034->mutex); - is_muted = IDT821034_ID_IS_OUT(id) ? - idt821034->amps.ch[ch].amp_out.is_muted : - idt821034->amps.ch[ch].amp_in.is_muted; - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) { + is_muted = IDT821034_ID_IS_OUT(id) ? + idt821034->amps.ch[ch].amp_out.is_muted : + idt821034->amps.ch[ch].amp_in.is_muted; + } ucontrol->value.integer.value[0] = !is_muted; @@ -521,7 +518,7 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, ch = IDT821034_ID_GET_CHAN(id); is_mute = !ucontrol->value.integer.value[0]; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); if (IDT821034_ID_IS_OUT(id)) { amp = &idt821034->amps.ch[ch].amp_out; @@ -531,21 +528,17 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, gain_type = IDT821034_GAIN_TX; } - if (amp->is_muted == is_mute) { - ret = 0; - goto end; - } + if (amp->is_muted == is_mute) + return 0; ret = idt821034_set_gain_channel(idt821034, ch, gain_type, is_mute ? 0 : amp->gain); if (ret) - goto end; + return ret; amp->is_muted = is_mute; - ret = 1; /* The value changed */ -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return 1; } static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -300, 1300); @@ -623,24 +616,20 @@ static int idt821034_power_event(struct snd_soc_dapm_widget *w, struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); unsigned int id = w->shift; u8 power, mask; - int ret; u8 ch; ch = IDT821034_ID_GET_CHAN(id); mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); power = idt821034_get_channel_power(idt821034, ch); if (SND_SOC_DAPM_EVENT_ON(event)) power |= mask; else power &= ~mask; - ret = idt821034_set_channel_power(idt821034, ch, power); - - mutex_unlock(&idt821034->mutex); - return ret; + return idt821034_set_channel_power(idt821034, ch, power); } static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = { @@ -717,9 +706,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, ch = 0; while (mask && ch < IDT821034_NB_CHANNEL) { if (mask & 0x1) { - mutex_lock(&idt821034->mutex); - ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot); - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) + ret = idt821034_set_channel_ts(idt821034, ch, + IDT821034_CH_RX, slot); if (ret) { dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n", ch, ret); @@ -742,9 +731,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, ch = 0; while (mask && ch < IDT821034_NB_CHANNEL) { if (mask & 0x1) { - mutex_lock(&idt821034->mutex); - ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot); - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) + ret = idt821034_set_channel_ts(idt821034, ch, + IDT821034_CH_TX, slot); if (ret) { dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n", ch, ret); @@ -769,9 +758,8 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); u8 conf; - int ret; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); conf = idt821034_get_codec_conf(idt821034); @@ -785,13 +773,10 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) default: dev_err(dai->dev, "Unsupported DAI format 0x%x\n", fmt & SND_SOC_DAIFMT_FORMAT_MASK); - ret = -EINVAL; - goto end; + return -EINVAL; } - ret = idt821034_set_codec_conf(idt821034, conf); -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return idt821034_set_codec_conf(idt821034, conf); } static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, @@ -800,9 +785,8 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, { struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); u8 conf; - int ret; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); conf = idt821034_get_codec_conf(idt821034); @@ -816,13 +800,10 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, default: dev_err(dai->dev, "Unsupported PCM format 0x%x\n", params_format(params)); - ret = -EINVAL; - goto end; + return -EINVAL; } - ret = idt821034_set_codec_conf(idt821034, conf); -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return idt821034_set_codec_conf(idt821034, conf); } static const unsigned int idt821034_sample_bits[] = {8}; @@ -897,11 +878,11 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) int ret; u8 i; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); ret = idt821034_set_codec_conf(idt821034, 0); if (ret) - goto end; + return ret; for (i = 0; i < IDT821034_NB_CHANNEL; i++) { idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW; @@ -909,24 +890,21 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX, idt821034->amps.ch[i].amp_out.gain); if (ret) - goto end; + return ret; idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW; idt821034->amps.ch[i].amp_in.is_muted = false; ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX, idt821034->amps.ch[i].amp_in.gain); if (ret) - goto end; + return ret; ret = idt821034_set_channel_power(idt821034, i, 0); if (ret) - goto end; + return ret; } - ret = 0; -end: - mutex_unlock(&idt821034->mutex); - return ret; + return 0; } static int idt821034_component_probe(struct snd_soc_component *component) @@ -965,7 +943,7 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, u8 slic_raw; int ret; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); slic_raw = idt821034_get_written_slic_raw(idt821034, ch); if (val) @@ -974,8 +952,6 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, slic_raw &= ~mask; ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); - mutex_unlock(&idt821034->mutex); - if (ret) dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", offset, ch, mask, ret); @@ -991,9 +967,8 @@ static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) u8 slic_raw; int ret; - mutex_lock(&idt821034->mutex); - ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) + ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); if (ret) { dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n", offset, ch, mask, ret); @@ -1015,9 +990,8 @@ static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset struct idt821034 *idt821034 = gpiochip_get_data(c); u8 slic_dir; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); slic_dir = idt821034_get_slic_conf(idt821034, ch); - mutex_unlock(&idt821034->mutex); return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; } @@ -1034,7 +1008,7 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN)) return -EPERM; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask; @@ -1044,7 +1018,6 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs offset, ch, mask, ret); } - mutex_unlock(&idt821034->mutex); return ret; } @@ -1060,7 +1033,7 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off if (ret) return ret; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask; @@ -1070,7 +1043,6 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off offset, ch, mask, ret); } - mutex_unlock(&idt821034->mutex); return ret; } @@ -1079,24 +1051,22 @@ static int idt821034_reset_gpio(struct idt821034 *idt821034) int ret; u8 i; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); /* IO0 and IO1 as input for all channels and output IO set to 0 */ for (i = 0; i < IDT821034_NB_CHANNEL; i++) { ret = idt821034_set_slic_conf(idt821034, i, IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN); if (ret) - goto end; + return ret; ret = idt821034_write_slic_raw(idt821034, i, 0); if (ret) - goto end; + return ret; } - ret = 0; -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return 0; } static int idt821034_gpio_init(struct idt821034 *idt821034) diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c index a3d6318c9050..dadb4a6c287f 100644 --- a/sound/soc/codecs/lochnagar-sc.c +++ b/sound/soc/codecs/lochnagar-sc.c @@ -137,15 +137,23 @@ static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt) return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBP_CFP); } +static const u64 lochnagar_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops lochnagar_sc_line_ops = { .startup = lochnagar_sc_line_startup, .shutdown = lochnagar_sc_line_shutdown, .set_fmt = lochnagar_sc_set_line_fmt, + .auto_selectable_formats = &lochnagar_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = { .startup = lochnagar_sc_startup, .set_fmt = lochnagar_sc_set_usb_fmt, + .auto_selectable_formats = &lochnagar_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver lochnagar_sc_dai[] = { diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c index 6e3b8d0897dd..7e59616ed7bc 100644 --- a/sound/soc/codecs/lpass-macro-common.c +++ b/sound/soc/codecs/lpass-macro-common.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2022, The Linux Foundation. All rights reserved. +#include <linux/cleanup.h> #include <linux/export.h> #include <linux/module.h> #include <linux/init.h> @@ -71,21 +72,16 @@ EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); void lpass_macro_set_codec_version(enum lpass_codec_version version) { - mutex_lock(&lpass_codec_mutex); + guard(mutex)(&lpass_codec_mutex); lpass_codec_version = version; - mutex_unlock(&lpass_codec_mutex); } EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version); enum lpass_codec_version lpass_macro_get_codec_version(void) { - enum lpass_codec_version ver; + guard(mutex)(&lpass_codec_mutex); - mutex_lock(&lpass_codec_mutex); - ver = lpass_codec_version; - mutex_unlock(&lpass_codec_mutex); - - return ver; + return lpass_codec_version; } EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version); diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 6233aa9f5bc6..282a6aaa9986 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -6,6 +6,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/pm_clock.h> #include <linux/pm_runtime.h> #include <linux/clk.h> #include <sound/soc.h> @@ -2030,9 +2031,10 @@ static struct snd_soc_dai_driver rx_macro_dai[] = { }, }; -static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) +static int rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) { struct regmap *regmap = rx->regmap; + int ret; if (mclk_enable) { if (rx->rx_mclk_users == 0) { @@ -2047,14 +2049,16 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) CDC_RX_FS_MCLK_CNT_EN_MASK, CDC_RX_FS_MCLK_CNT_ENABLE); regcache_mark_dirty(regmap); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) + return ret; } rx->rx_mclk_users++; } else { if (rx->rx_mclk_users <= 0) { dev_err(rx->dev, "%s: clock already disabled\n", __func__); rx->rx_mclk_users = 0; - return; + return 0; } rx->rx_mclk_users--; if (rx->rx_mclk_users == 0) { @@ -2068,6 +2072,8 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) CDC_RX_CLK_MCLK2_EN_MASK, 0x0); } } + + return 0; } static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, @@ -2079,11 +2085,9 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - rx_macro_mclk_enable(rx, true); - break; + return rx_macro_mclk_enable(rx, true); case SND_SOC_DAPM_POST_PMD: - rx_macro_mclk_enable(rx, false); - break; + return rx_macro_mclk_enable(rx, false); default: dev_err(component->dev, "%s: invalid DAPM event %d\n", __func__, event); ret = -EINVAL; @@ -3671,14 +3675,16 @@ static int swclk_gate_enable(struct clk_hw *hw) struct rx_macro *rx = to_rx_macro(hw); int ret; - ret = clk_prepare_enable(rx->mclk); + ret = pm_runtime_resume_and_get(rx->dev); + if (ret < 0) + return ret; + + ret = rx_macro_mclk_enable(rx, true); if (ret) { - dev_err(rx->dev, "unable to prepare mclk\n"); + clk_disable_unprepare(rx->mclk); return ret; } - rx_macro_mclk_enable(rx, true); - regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, CDC_RX_SWR_CLK_EN_MASK, 1); @@ -3693,7 +3699,7 @@ static void swclk_gate_disable(struct clk_hw *hw) CDC_RX_SWR_CLK_EN_MASK, 0); rx_macro_mclk_enable(rx, false); - clk_disable_unprepare(rx->mclk); + pm_runtime_put_autosuspend(rx->dev); } static int swclk_gate_is_enabled(struct clk_hw *hw) @@ -3864,28 +3870,31 @@ static int rx_macro_probe(struct platform_device *pdev) rx->dev = dev; /* set MCLK and NPL rates */ - clk_set_rate(rx->mclk, MCLK_FREQ); - clk_set_rate(rx->npl, MCLK_FREQ); - - ret = clk_prepare_enable(rx->macro); + ret = clk_set_rate(rx->mclk, MCLK_FREQ); if (ret) return ret; - ret = clk_prepare_enable(rx->dcodec); + ret = clk_set_rate(rx->npl, MCLK_FREQ); if (ret) - goto err_dcodec; + return ret; - ret = clk_prepare_enable(rx->mclk); + ret = devm_pm_clk_create(dev); if (ret) - goto err_mclk; + return ret; - ret = clk_prepare_enable(rx->npl); + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + return ret; + + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_npl; + return ret; - ret = clk_prepare_enable(rx->fsgen); + ret = pm_runtime_resume_and_get(dev); if (ret) - goto err_fsgen; + return ret; /* reset swr block */ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, @@ -3902,46 +3911,25 @@ static int rx_macro_probe(struct platform_device *pdev) rx_macro_dai, ARRAY_SIZE(rx_macro_dai)); if (ret) - goto err_clkout; - - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = rx_macro_register_mclk_output(rx); if (ret) - goto err_clkout; + goto err_rpm_put; + + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); return 0; -err_clkout: - clk_disable_unprepare(rx->fsgen); -err_fsgen: - clk_disable_unprepare(rx->npl); -err_npl: - clk_disable_unprepare(rx->mclk); -err_mclk: - clk_disable_unprepare(rx->dcodec); -err_dcodec: - clk_disable_unprepare(rx->macro); +err_rpm_put: + if (pm_runtime_put_sync_suspend(dev) < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); return ret; } -static void rx_macro_remove(struct platform_device *pdev) -{ - struct rx_macro *rx = dev_get_drvdata(&pdev->dev); - - clk_disable_unprepare(rx->mclk); - clk_disable_unprepare(rx->npl); - clk_disable_unprepare(rx->fsgen); - clk_disable_unprepare(rx->macro); - clk_disable_unprepare(rx->dcodec); -} - static const struct of_device_id rx_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-rx-macro", @@ -3969,13 +3957,17 @@ MODULE_DEVICE_TABLE(of, rx_macro_dt_match); static int rx_macro_runtime_suspend(struct device *dev) { struct rx_macro *rx = dev_get_drvdata(dev); + int ret; regcache_cache_only(rx->regmap, true); - regcache_mark_dirty(rx->regmap); - clk_disable_unprepare(rx->fsgen); - clk_disable_unprepare(rx->npl); - clk_disable_unprepare(rx->mclk); + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(rx->regmap, false); + return ret; + } + + regcache_mark_dirty(rx->regmap); return 0; } @@ -3985,33 +3977,23 @@ static int rx_macro_runtime_resume(struct device *dev) struct rx_macro *rx = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(rx->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(dev, "unable to prepare mclk\n"); + regcache_cache_only(rx->regmap, true); + regcache_mark_dirty(rx->regmap); return ret; } - ret = clk_prepare_enable(rx->npl); - if (ret) { - dev_err(dev, "unable to prepare mclkx2\n"); - goto err_npl; - } - - ret = clk_prepare_enable(rx->fsgen); + regcache_cache_only(rx->regmap, false); + ret = regcache_sync(rx->regmap); if (ret) { - dev_err(dev, "unable to prepare fsgen\n"); - goto err_fsgen; + regcache_cache_only(rx->regmap, true); + regcache_mark_dirty(rx->regmap); + pm_clk_suspend(dev); + return ret; } - regcache_cache_only(rx->regmap, false); - regcache_sync(rx->regmap); return 0; -err_fsgen: - clk_disable_unprepare(rx->npl); -err_npl: - clk_disable_unprepare(rx->mclk); - - return ret; } static const struct dev_pm_ops rx_macro_pm_ops = { @@ -4026,7 +4008,6 @@ static struct platform_driver rx_macro_driver = { .pm = pm_ptr(&rx_macro_pm_ops), }, .probe = rx_macro_probe, - .remove = rx_macro_remove, }; module_platform_driver(rx_macro_driver); diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 0cbf50647ff5..a8e83eb60ba2 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -6,6 +6,7 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/pm_clock.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <sound/soc.h> @@ -614,6 +615,7 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, bool mclk_enable) { struct regmap *regmap = tx->regmap; + int ret; if (mclk_enable) { if (tx->tx_mclk_users == 0) { @@ -626,7 +628,9 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, CDC_TX_FS_CNT_EN_MASK, CDC_TX_FS_CNT_ENABLE); regcache_mark_dirty(regmap); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) + return ret; } tx->tx_mclk_users++; } else { @@ -739,11 +743,9 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - tx_macro_mclk_enable(tx, true); - break; + return tx_macro_mclk_enable(tx, true); case SND_SOC_DAPM_POST_PMD: - tx_macro_mclk_enable(tx, false); - break; + return tx_macro_mclk_enable(tx, false); default: break; } @@ -2149,17 +2151,20 @@ static int swclk_gate_enable(struct clk_hw *hw) struct regmap *regmap = tx->regmap; int ret; - ret = clk_prepare_enable(tx->mclk); + ret = pm_runtime_resume_and_get(tx->dev); + if (ret < 0) + return ret; + + ret = tx_macro_mclk_enable(tx, true); if (ret) { - dev_err(tx->dev, "failed to enable mclk\n"); + pm_runtime_put_autosuspend(tx->dev); return ret; } - tx_macro_mclk_enable(tx, true); - regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_CLK_EN_MASK, CDC_TX_SWR_CLK_ENABLE); + return 0; } @@ -2172,7 +2177,7 @@ static void swclk_gate_disable(struct clk_hw *hw) CDC_TX_SWR_CLK_EN_MASK, 0x0); tx_macro_mclk_enable(tx, false); - clk_disable_unprepare(tx->mclk); + pm_runtime_put_autosuspend(tx->dev); } static int swclk_gate_is_enabled(struct clk_hw *hw) @@ -2315,28 +2320,31 @@ static int tx_macro_probe(struct platform_device *pdev) tx->active_decimator[TX_MACRO_AIF3_CAP] = -1; /* set MCLK and NPL rates */ - clk_set_rate(tx->mclk, MCLK_FREQ); - clk_set_rate(tx->npl, MCLK_FREQ); - - ret = clk_prepare_enable(tx->macro); + ret = clk_set_rate(tx->mclk, MCLK_FREQ); if (ret) goto err; - ret = clk_prepare_enable(tx->dcodec); + ret = clk_set_rate(tx->npl, MCLK_FREQ); if (ret) - goto err_dcodec; + goto err; - ret = clk_prepare_enable(tx->mclk); + ret = devm_pm_clk_create(dev); if (ret) - goto err_mclk; + goto err; - ret = clk_prepare_enable(tx->npl); - if (ret) - goto err_npl; + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + goto err; - ret = clk_prepare_enable(tx->fsgen); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_fsgen; + goto err; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err; /* reset soundwire block */ @@ -2356,30 +2364,21 @@ static int tx_macro_probe(struct platform_device *pdev) tx_macro_dai, ARRAY_SIZE(tx_macro_dai)); if (ret) - goto err_clkout; - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = tx_macro_register_mclk_output(tx); if (ret) - goto err_clkout; + goto err_rpm_put; + + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); return 0; -err_clkout: - clk_disable_unprepare(tx->fsgen); -err_fsgen: - clk_disable_unprepare(tx->npl); -err_npl: - clk_disable_unprepare(tx->mclk); -err_mclk: - clk_disable_unprepare(tx->dcodec); -err_dcodec: - clk_disable_unprepare(tx->macro); +err_rpm_put: + if (pm_runtime_put_sync_suspend(dev) < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); err: lpass_macro_pds_exit(tx->pds); @@ -2390,25 +2389,23 @@ static void tx_macro_remove(struct platform_device *pdev) { struct tx_macro *tx = dev_get_drvdata(&pdev->dev); - clk_disable_unprepare(tx->macro); - clk_disable_unprepare(tx->dcodec); - clk_disable_unprepare(tx->mclk); - clk_disable_unprepare(tx->npl); - clk_disable_unprepare(tx->fsgen); - lpass_macro_pds_exit(tx->pds); } static int tx_macro_runtime_suspend(struct device *dev) { struct tx_macro *tx = dev_get_drvdata(dev); + int ret; regcache_cache_only(tx->regmap, true); - regcache_mark_dirty(tx->regmap); - clk_disable_unprepare(tx->fsgen); - clk_disable_unprepare(tx->npl); - clk_disable_unprepare(tx->mclk); + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(tx->regmap, false); + return ret; + } + + regcache_mark_dirty(tx->regmap); return 0; } @@ -2418,34 +2415,23 @@ static int tx_macro_runtime_resume(struct device *dev) struct tx_macro *tx = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(tx->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(dev, "unable to prepare mclk\n"); + regcache_cache_only(tx->regmap, true); + regcache_mark_dirty(tx->regmap); return ret; } - ret = clk_prepare_enable(tx->npl); - if (ret) { - dev_err(dev, "unable to prepare npl\n"); - goto err_npl; - } - - ret = clk_prepare_enable(tx->fsgen); + regcache_cache_only(tx->regmap, false); + ret = regcache_sync(tx->regmap); if (ret) { - dev_err(dev, "unable to prepare fsgen\n"); - goto err_fsgen; + regcache_cache_only(tx->regmap, true); + regcache_mark_dirty(tx->regmap); + pm_clk_suspend(dev); + return ret; } - regcache_cache_only(tx->regmap, false); - regcache_sync(tx->regmap); - return 0; -err_fsgen: - clk_disable_unprepare(tx->npl); -err_npl: - clk_disable_unprepare(tx->mclk); - - return ret; } static const struct dev_pm_ops tx_macro_pm_ops = { diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 58a5798823d7..dbc5795b9273 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -10,6 +10,7 @@ #include <linux/of_clk.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pm_clock.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -1351,34 +1352,43 @@ static int fsgen_gate_enable(struct clk_hw *hw) { struct va_macro *va = to_va_macro(hw); struct regmap *regmap = va->regmap; - int ret; + int ret, rpm_ret; - if (va->has_swr_master) { - ret = clk_prepare_enable(va->mclk); - if (ret) - return ret; - } + ret = pm_runtime_resume_and_get(va->dev); + if (ret < 0) + return ret; ret = va_macro_mclk_enable(va, true); + if (ret) { + rpm_ret = pm_runtime_put_autosuspend(va->dev); + if (rpm_ret < 0) + dev_warn(va->dev, + "runtime PM put failed in fsgen enable unwind: %d\n", + rpm_ret); + return ret; + } if (va->has_swr_master) regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); - return ret; + return 0; } static void fsgen_gate_disable(struct clk_hw *hw) { struct va_macro *va = to_va_macro(hw); struct regmap *regmap = va->regmap; + int ret; if (va->has_swr_master) regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, CDC_VA_SWR_CLK_EN_MASK, 0x0); va_macro_mclk_enable(va, false); - if (va->has_swr_master) - clk_disable_unprepare(va->mclk); + + ret = pm_runtime_put_autosuspend(va->dev); + if (ret < 0) + dev_warn(va->dev, "runtime PM put failed in fsgen disable: %d\n", ret); } static int fsgen_gate_is_enabled(struct clk_hw *hw) @@ -1539,6 +1549,7 @@ static int va_macro_probe(struct platform_device *pdev) void __iomem *base; u32 sample_rate = 0; int ret; + int rpm_ret; va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL); if (!va) @@ -1594,7 +1605,9 @@ static int va_macro_probe(struct platform_device *pdev) va->has_npl_clk = data->has_npl_clk; /* mclk rate */ - clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); + ret = clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); + if (ret) + goto err; if (va->has_npl_clk) { va->npl = devm_clk_get(dev, "npl"); @@ -1603,25 +1616,29 @@ static int va_macro_probe(struct platform_device *pdev) goto err; } - clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); + ret = clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); + if (ret) + goto err; } - ret = clk_prepare_enable(va->macro); + ret = devm_pm_clk_create(dev); if (ret) goto err; - ret = clk_prepare_enable(va->dcodec); - if (ret) - goto err_dcodec; + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + goto err; - ret = clk_prepare_enable(va->mclk); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_mclk; + goto err; - if (va->has_npl_clk) { - ret = clk_prepare_enable(va->npl); - if (ret) - goto err_npl; + rpm_ret = pm_runtime_resume_and_get(dev); + if (rpm_ret < 0) { + ret = rpm_ret; + goto err; } /** @@ -1634,7 +1651,7 @@ static int va_macro_probe(struct platform_device *pdev) /* read version from register */ ret = va_macro_set_lpass_codec_version(va); if (ret) - goto err_clkout; + goto err_rpm_put; } if (va->has_swr_master) { @@ -1664,35 +1681,28 @@ static int va_macro_probe(struct platform_device *pdev) va_macro_dais, ARRAY_SIZE(va_macro_dais)); if (ret) - goto err_clkout; - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = va_macro_register_fsgen_output(va); if (ret) - goto err_clkout; + goto err_rpm_put; va->fsgen = devm_clk_hw_get_clk(dev, &va->hw, "fsgen"); if (IS_ERR(va->fsgen)) { ret = PTR_ERR(va->fsgen); - goto err_clkout; + goto err_rpm_put; } + rpm_ret = pm_runtime_put_autosuspend(dev); + if (rpm_ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", rpm_ret); + return 0; -err_clkout: - if (va->has_npl_clk) - clk_disable_unprepare(va->npl); -err_npl: - clk_disable_unprepare(va->mclk); -err_mclk: - clk_disable_unprepare(va->dcodec); -err_dcodec: - clk_disable_unprepare(va->macro); +err_rpm_put: + rpm_ret = pm_runtime_put_sync_suspend(dev); + if (rpm_ret < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind: %d\n", rpm_ret); err: lpass_macro_pds_exit(va->pds); @@ -1703,27 +1713,23 @@ static void va_macro_remove(struct platform_device *pdev) { struct va_macro *va = dev_get_drvdata(&pdev->dev); - if (va->has_npl_clk) - clk_disable_unprepare(va->npl); - - clk_disable_unprepare(va->mclk); - clk_disable_unprepare(va->dcodec); - clk_disable_unprepare(va->macro); - lpass_macro_pds_exit(va->pds); } static int va_macro_runtime_suspend(struct device *dev) { struct va_macro *va = dev_get_drvdata(dev); + int ret; regcache_cache_only(va->regmap, true); - regcache_mark_dirty(va->regmap); - if (va->has_npl_clk) - clk_disable_unprepare(va->npl); + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(va->regmap, false); + return ret; + } - clk_disable_unprepare(va->mclk); + regcache_mark_dirty(va->regmap); return 0; } @@ -1731,25 +1737,28 @@ static int va_macro_runtime_suspend(struct device *dev) static int va_macro_runtime_resume(struct device *dev) { struct va_macro *va = dev_get_drvdata(dev); - int ret; + int ret, sret; - ret = clk_prepare_enable(va->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(va->dev, "unable to prepare mclk\n"); + regcache_cache_only(va->regmap, true); + regcache_mark_dirty(va->regmap); return ret; } - if (va->has_npl_clk) { - ret = clk_prepare_enable(va->npl); - if (ret) { - clk_disable_unprepare(va->mclk); - dev_err(va->dev, "unable to prepare npl\n"); - return ret; - } - } - regcache_cache_only(va->regmap, false); - regcache_sync(va->regmap); + + ret = regcache_sync(va->regmap); + if (ret) { + regcache_cache_only(va->regmap, true); + regcache_mark_dirty(va->regmap); + sret = pm_clk_suspend(dev); + if (sret) + dev_err(va->dev, + "failed to suspend clocks after regcache sync failure: %d\n", + sret); + return ret; + } return 0; } diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index af521e0988db..cfd2ac0a6cda 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -12,6 +12,7 @@ #include <linux/clk-provider.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <linux/pm_clock.h> #include <linux/pm_runtime.h> #include <linux/of_platform.h> #include <sound/tlv.h> @@ -2529,15 +2530,13 @@ static const struct snd_soc_dapm_route wsa_audio_map[] = { static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) { struct regmap *regmap = wsa->regmap; + int ret; if (enable) { - int ret; - - ret = clk_prepare_enable(wsa->mclk); - if (ret) { - dev_err(wsa->dev, "failed to enable mclk\n"); + ret = pm_runtime_resume_and_get(wsa->dev); + if (ret < 0) return ret; - } + wsa_macro_mclk_enable(wsa, true); regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, @@ -2548,7 +2547,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, CDC_WSA_SWR_CLK_EN_MASK, 0); wsa_macro_mclk_enable(wsa, false); - clk_disable_unprepare(wsa->mclk); + + ret = pm_runtime_put_autosuspend(wsa->dev); + if (ret < 0) + dev_warn(wsa->dev, "runtime PM put failed: %d\n", ret); } return 0; @@ -2656,7 +2658,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) init.num_parents = 1; wsa->hw.init = &init; hw = &wsa->hw; - ret = clk_hw_register(wsa->dev, hw); + ret = devm_clk_hw_register(wsa->dev, hw); if (ret) return ret; @@ -2771,28 +2773,31 @@ static int wsa_macro_probe(struct platform_device *pdev) wsa->dev = dev; /* set MCLK and NPL rates */ - clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); - clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); - - ret = clk_prepare_enable(wsa->macro); + ret = clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); if (ret) - goto err; + return ret; - ret = clk_prepare_enable(wsa->dcodec); + ret = clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); if (ret) - goto err_dcodec; + return ret; - ret = clk_prepare_enable(wsa->mclk); + ret = devm_pm_clk_create(dev); if (ret) - goto err_mclk; + return ret; - ret = clk_prepare_enable(wsa->npl); - if (ret) - goto err_npl; + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + return ret; - ret = clk_prepare_enable(wsa->fsgen); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_fsgen; + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; /* reset swr ip */ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, @@ -2809,56 +2814,37 @@ static int wsa_macro_probe(struct platform_device *pdev) wsa_macro_dai, ARRAY_SIZE(wsa_macro_dai)); if (ret) - goto err_clkout; - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = wsa_macro_register_mclk_output(wsa); if (ret) - goto err_clkout; + goto err_rpm_put; - return 0; + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); -err_clkout: - clk_disable_unprepare(wsa->fsgen); -err_fsgen: - clk_disable_unprepare(wsa->npl); -err_npl: - clk_disable_unprepare(wsa->mclk); -err_mclk: - clk_disable_unprepare(wsa->dcodec); -err_dcodec: - clk_disable_unprepare(wsa->macro); -err: + return 0; +err_rpm_put: + if (pm_runtime_put_sync_suspend(dev) < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); return ret; - -} - -static void wsa_macro_remove(struct platform_device *pdev) -{ - struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev); - - clk_disable_unprepare(wsa->macro); - clk_disable_unprepare(wsa->dcodec); - clk_disable_unprepare(wsa->mclk); - clk_disable_unprepare(wsa->npl); - clk_disable_unprepare(wsa->fsgen); } static int wsa_macro_runtime_suspend(struct device *dev) { struct wsa_macro *wsa = dev_get_drvdata(dev); + int ret; regcache_cache_only(wsa->regmap, true); - regcache_mark_dirty(wsa->regmap); - clk_disable_unprepare(wsa->fsgen); - clk_disable_unprepare(wsa->npl); - clk_disable_unprepare(wsa->mclk); + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(wsa->regmap, false); + return ret; + } + + regcache_mark_dirty(wsa->regmap); return 0; } @@ -2866,36 +2852,29 @@ static int wsa_macro_runtime_suspend(struct device *dev) static int wsa_macro_runtime_resume(struct device *dev) { struct wsa_macro *wsa = dev_get_drvdata(dev); - int ret; + int ret, sret; - ret = clk_prepare_enable(wsa->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(dev, "unable to prepare mclk\n"); + regcache_cache_only(wsa->regmap, true); + regcache_mark_dirty(wsa->regmap); return ret; } + regcache_cache_only(wsa->regmap, false); - ret = clk_prepare_enable(wsa->npl); - if (ret) { - dev_err(dev, "unable to prepare mclkx2\n"); - goto err_npl; - } - - ret = clk_prepare_enable(wsa->fsgen); + ret = regcache_sync(wsa->regmap); if (ret) { - dev_err(dev, "unable to prepare fsgen\n"); - goto err_fsgen; + regcache_cache_only(wsa->regmap, true); + regcache_mark_dirty(wsa->regmap); + sret = pm_clk_suspend(dev); + if (sret) + dev_err(dev, + "failed to suspend clocks after regcache sync failure: %d\n", + sret); + return ret; } - regcache_cache_only(wsa->regmap, false); - regcache_sync(wsa->regmap); - return 0; -err_fsgen: - clk_disable_unprepare(wsa->npl); -err_npl: - clk_disable_unprepare(wsa->mclk); - - return ret; } static const struct dev_pm_ops wsa_macro_pm_ops = { @@ -2929,7 +2908,6 @@ static struct platform_driver wsa_macro_driver = { .pm = pm_ptr(&wsa_macro_pm_ops), }, .probe = wsa_macro_probe, - .remove = wsa_macro_remove, }; module_platform_driver(wsa_macro_driver); diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c index 98d72db599d8..58255844b4df 100644 --- a/sound/soc/codecs/madera.c +++ b/sound/soc/codecs/madera.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/gcd.h> #include <linux/module.h> @@ -513,7 +514,7 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, * We can't rely on the DAPM mutex for locking because we need a lock * that can safely be called in hw_params */ - mutex_lock(&priv->rate_lock); + guard(mutex)(&priv->rate_lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -532,8 +533,6 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, madera_debug_dump_domain_groups(priv); - mutex_unlock(&priv->rate_lock); - return 0; } EXPORT_SYMBOL_GPL(madera_domain_clk_ev); @@ -875,9 +874,8 @@ static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol, const int adsp_num = e->shift_l; int item; - mutex_lock(&priv->rate_lock); - cached_rate = priv->adsp_rate_cache[adsp_num]; - mutex_unlock(&priv->rate_lock); + scoped_guard(mutex, &priv->rate_lock) + cached_rate = priv->adsp_rate_cache[adsp_num]; item = snd_soc_enum_val_to_item(e, cached_rate); ucontrol->value.enumerated.item[0] = item; @@ -893,7 +891,6 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; const int adsp_num = e->shift_l; const unsigned int item = ucontrol->value.enumerated.item[0]; - int ret = 0; if (item >= e->items) return -EINVAL; @@ -903,22 +900,20 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, * maintain consistent behaviour that rate domains cannot be changed * while in use since this is a hardware requirement */ - mutex_lock(&priv->rate_lock); + guard(mutex)(&priv->rate_lock); if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) { dev_warn(priv->madera->dev, "Cannot change '%s' while in use by active audio paths\n", kcontrol->id.name); - ret = -EBUSY; + return -EBUSY; } else if (priv->adsp_rate_cache[adsp_num] != e->values[item]) { /* Volatile register so defer until the codec is powered up */ priv->adsp_rate_cache[adsp_num] = e->values[item]; - ret = 1; + return 1; } - mutex_unlock(&priv->rate_lock); - - return ret; + return 0; } static const struct soc_enum madera_adsp_rate_enum[] = { @@ -1061,15 +1056,13 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, * Prevent the domain powering up while we're checking whether it's * safe to change rate domain */ - mutex_lock(&priv->rate_lock); + guard(mutex)(&priv->rate_lock); val = snd_soc_component_read(component, e->reg); val >>= e->shift_l; val &= e->mask; - if (snd_soc_enum_item_to_val(e, item) == val) { - ret = 0; - goto out; - } + if (snd_soc_enum_item_to_val(e, item) == val) + return 0; if (!madera_can_change_grp_rate(priv, e->reg)) { dev_warn(priv->madera->dev, @@ -1082,8 +1075,6 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, ret = snd_soc_put_enum_double(kcontrol, ucontrol); madera_spin_sysclk(priv); } -out: - mutex_unlock(&priv->rate_lock); return ret; } @@ -3041,12 +3032,11 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK)) return 0; - mutex_lock(&priv->rate_lock); + guard(mutex)(&priv->rate_lock); if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) { madera_aif_warn(dai, "Cannot change rate while active\n"); - ret = -EBUSY; - goto out; + return -EBUSY; } /* Guard the rate change with SYSCLK cycles */ @@ -3055,9 +3045,6 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, MADERA_AIF1_RATE_MASK, tar); madera_spin_sysclk(priv); -out: - mutex_unlock(&priv->rate_lock); - return ret; } @@ -3345,6 +3332,16 @@ static int madera_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, return 0; } +static const u64 madera_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + const struct snd_soc_dai_ops madera_dai_ops = { .startup = &madera_startup, .set_fmt = &madera_set_fmt, @@ -3352,6 +3349,8 @@ const struct snd_soc_dai_ops madera_dai_ops = { .hw_params = &madera_hw_params, .set_sysclk = &madera_dai_set_sysclk, .set_tristate = &madera_set_tristate, + .auto_selectable_formats = &madera_selectable_formats, + .num_auto_selectable_formats = 1, }; EXPORT_SYMBOL_GPL(madera_dai_ops); diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 57bd5a1c34c8..64dc442e9066 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2341,6 +2341,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data) * * @component: MAX98090 component * @jack: jack to report detection events on + * @data: can be used if codec driver need extra data for configuring jack * * Enable microphone detection via IRQ on the MAX98090. If GPIOs are * being used to bring out signals to the processor then only platform @@ -2646,12 +2647,18 @@ static void max98090_i2c_remove(struct i2c_client *client) static int max98090_runtime_resume(struct device *dev) { struct max98090_priv *max98090 = dev_get_drvdata(dev); + int ret; regcache_cache_only(max98090->regmap, false); max98090_reset(max98090); - regcache_sync(max98090->regmap); + ret = regcache_sync(max98090->regmap); + if (ret < 0) { + regcache_cache_only(max98090->regmap, true); + regcache_mark_dirty(max98090->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 130b6694a634..d93436046849 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -5,6 +5,7 @@ * Copyright 2011 Maxim Integrated Products */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -1532,15 +1533,17 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); - mutex_lock(&max98095->lock); - snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); - m98095_eq_band(component, channel, 0, coef_set->band1); - m98095_eq_band(component, channel, 1, coef_set->band2); - m98095_eq_band(component, channel, 2, coef_set->band3); - m98095_eq_band(component, channel, 3, coef_set->band4); - m98095_eq_band(component, channel, 4, coef_set->band5); - snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); - mutex_unlock(&max98095->lock); + scoped_guard(mutex, &max98095->lock) { + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, + M98095_SEG, M98095_SEG); + m98095_eq_band(component, channel, 0, coef_set->band1); + m98095_eq_band(component, channel, 1, coef_set->band2); + m98095_eq_band(component, channel, 2, coef_set->band3); + m98095_eq_band(component, channel, 3, coef_set->band4); + m98095_eq_band(component, channel, 4, coef_set->band5); + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, + M98095_SEG, 0); + } /* Restore the original on/off state */ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); @@ -1683,12 +1686,14 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); - mutex_lock(&max98095->lock); - snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); - m98095_biquad_band(component, channel, 0, coef_set->band1); - m98095_biquad_band(component, channel, 1, coef_set->band2); - snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); - mutex_unlock(&max98095->lock); + scoped_guard(mutex, &max98095->lock) { + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, + M98095_SEG, M98095_SEG); + m98095_biquad_band(component, channel, 0, coef_set->band1); + m98095_biquad_band(component, channel, 1, coef_set->band2); + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, + M98095_SEG, 0); + } /* Restore the original on/off state */ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index 099dc5bf6195..0e32b7a94cb0 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -100,7 +100,12 @@ static int max98363_resume(struct device *dev) return ret; regcache_cache_only(max98363->regmap, false); - regcache_sync(max98363->regmap); + ret = regcache_sync(max98363->regmap); + if (ret) { + regcache_cache_only(max98363->regmap, true); + regcache_mark_dirty(max98363->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index 8805bd01153c..cbd7ebffa6d6 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -488,10 +488,17 @@ static int max98373_suspend(struct device *dev) static int max98373_resume(struct device *dev) { struct max98373_priv *max98373 = dev_get_drvdata(dev); + int ret; regcache_cache_only(max98373->regmap, false); max98373_reset(max98373, dev); - regcache_sync(max98373->regmap); + ret = regcache_sync(max98373->regmap); + if (ret) { + regcache_cache_only(max98373->regmap, true); + regcache_mark_dirty(max98373->regmap); + return ret; + } + return 0; } diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 8fe9c58e1a62..638ef343813b 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -277,7 +277,12 @@ static int max98373_resume(struct device *dev) } regcache_cache_only(max98373->regmap, false); - regcache_sync(max98373->regmap); + ret = regcache_sync(max98373->regmap); + if (ret) { + regcache_cache_only(max98373->regmap, true); + regcache_mark_dirty(max98373->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c index a4c57152d25f..d97dab41bf0f 100644 --- a/sound/soc/codecs/max98388.c +++ b/sound/soc/codecs/max98388.c @@ -863,10 +863,16 @@ static int max98388_suspend(struct device *dev) static int max98388_resume(struct device *dev) { struct max98388_priv *max98388 = dev_get_drvdata(dev); + int ret; regcache_cache_only(max98388->regmap, false); max98388_reset(max98388, dev); - regcache_sync(max98388->regmap); + ret = regcache_sync(max98388->regmap); + if (ret) { + regcache_cache_only(max98388->regmap, true); + regcache_mark_dirty(max98388->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 2bbedf84ee5d..2295fc057c71 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -788,7 +788,6 @@ static int max98390_dsm_init(struct snd_soc_component *component) const char *vendor, *product; struct max98390_priv *max98390 = snd_soc_component_get_drvdata(component); - const struct firmware *fw; char *dsm_param; vendor = dmi_get_system_info(DMI_SYS_VENDOR); @@ -805,6 +804,8 @@ static int max98390_dsm_init(struct snd_soc_component *component) snprintf(filename, sizeof(filename), "%s", max98390->dsm_param_name); } + + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, filename, component->dev); if (ret) { ret = request_firmware(&fw, "dsm_param.bin", component->dev); @@ -812,7 +813,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) ret = request_firmware(&fw, "dsmparam.bin", component->dev); if (ret) - goto err; + return ret; } } @@ -822,8 +823,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) { dev_err(component->dev, "param fw is invalid.\n"); - ret = -EINVAL; - goto err_alloc; + return -EINVAL; } dsm_param = (char *)fw->data; param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8; @@ -833,8 +833,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) { dev_err(component->dev, "param fw is invalid.\n"); - ret = -EINVAL; - goto err_alloc; + return -EINVAL; } regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80); dsm_param += MAX98390_DSM_PAYLOAD_OFFSET; @@ -842,10 +841,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) dsm_param, param_size); regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01); -err_alloc: - release_firmware(fw); -err: - return ret; + return 0; } static void max98390_init_regs(struct snd_soc_component *component) @@ -952,11 +948,17 @@ static int max98390_suspend(struct device *dev) static int max98390_resume(struct device *dev) { struct max98390_priv *max98390 = dev_get_drvdata(dev); + int ret; dev_dbg(dev, "%s:Enter\n", __func__); regcache_cache_only(max98390->regmap, false); - regcache_sync(max98390->regmap); + ret = regcache_sync(max98390->regmap); + if (ret) { + regcache_cache_only(max98390->regmap, true); + regcache_mark_dirty(max98390->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index 9c1d7213410c..e5a2fe824c03 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -1600,19 +1600,37 @@ static int max98396_resume(struct device *dev) if (max98396->pvdd) { ret = regulator_enable(max98396->pvdd); if (ret < 0) - return ret; + goto err_core_supplies; } if (max98396->vbat) { ret = regulator_enable(max98396->vbat); if (ret < 0) - return ret; + goto err_pvdd; } regcache_cache_only(max98396->regmap, false); max98396_reset(max98396, dev); - regcache_sync(max98396->regmap); + ret = regcache_sync(max98396->regmap); + if (ret < 0) { + regcache_cache_only(max98396->regmap, true); + regcache_mark_dirty(max98396->regmap); + goto err_vbat; + } + return 0; + +err_vbat: + if (max98396->vbat) + regulator_disable(max98396->vbat); +err_pvdd: + if (max98396->pvdd) + regulator_disable(max98396->pvdd); +err_core_supplies: + regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + + return ret; } static const struct dev_pm_ops max98396_pm = { diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 0d7ac37850bb..20e288e38669 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -48,7 +48,6 @@ static int max9860_dvddio_event(struct notifier_block *nb, } static const struct reg_default max9860_reg_defaults[] = { - { MAX9860_PWRMAN, 0x00 }, { MAX9860_INTEN, 0x00 }, { MAX9860_SYSCLK, 0x00 }, { MAX9860_AUDIOCLKHIGH, 0x00 }, @@ -62,6 +61,7 @@ static const struct reg_default max9860_reg_defaults[] = { { MAX9860_MICGAIN, 0x00 }, { MAX9860_MICADC, 0x00 }, { MAX9860_NOISEGATE, 0x00 }, + { MAX9860_PWRMAN, 0x00 }, }; static bool max9860_readable(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 65e6fdb30eec..524300d8fb0b 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -742,11 +742,17 @@ static int max98927_suspend(struct device *dev) static int max98927_resume(struct device *dev) { struct max98927_priv *max98927 = dev_get_drvdata(dev); + int ret; regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET); regcache_cache_only(max98927->regmap, false); - regcache_sync(max98927->regmap); + ret = regcache_sync(max98927->regmap); + if (ret) { + regcache_cache_only(max98927->regmap, true); + regcache_mark_dirty(max98927->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 8a14626d43ff..7209b47c2029 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -224,7 +224,6 @@ static const struct reg_default ml26124_reg[] = { /* Analog Path Control Register */ {0x54, 0x00}, /* Speaker AMP Output Control */ {0x5a, 0x00}, /* Mic IF Control */ - {0xe8, 0x01}, /* Mic Select Control */ /* Audio Interface Control Register */ {0x60, 0x00}, /* SAI-Trans Control */ @@ -287,6 +286,9 @@ static const struct reg_default ml26124_reg[] = { {0xd0, 0x01}, /* VIDEO AMP Gain Control */ {0xd2, 0x01}, /* VIDEO AMP Setup 1 */ {0xd4, 0x01}, /* VIDEO AMP Control2 */ + + /* Analog Path Control Register */ + {0xe8, 0x01}, /* Mic Select Control */ }; /* Get sampling rate value of sampling rate setting register (0x0) */ diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 9ca381812975..87f8a47cc293 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -217,9 +217,11 @@ #define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195) #define CDC_A_RX_HPH_BIAS_CNP (0xf196) #define CDC_A_RX_HPH_CNP_EN (0xf197) +#define CDC_A_RX_HPH_L_TEST (0xf19A) #define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B) #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1) #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1) +#define CDC_A_RX_HPH_R_TEST (0xf19C) #define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D) #define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1) #define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1) @@ -232,6 +234,8 @@ #define RX_EAR_CTL_PA_SEL_MASK BIT(7) #define RX_EAR_CTL_PA_SEL BIT(7) +#define CDC_A_RX_EAR_STATUS (0xf1A1) + #define CDC_A_SPKR_DAC_CTL (0xf1B0) #define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) #define SPKR_DAC_CTL_DAC_RESET_NORMAL 0 @@ -250,6 +254,7 @@ SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \ SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \ SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET) +#define CDC_A_SPKR_ANA_BIAS_SET (0xf1B3) #define CDC_A_SPKR_OCP_CTL (0xf1B4) #define CDC_A_SPKR_PWRSTG_CTL (0xf1B5) #define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0) @@ -264,6 +269,7 @@ #define CDC_A_SPKR_DRV_DBG (0xf1B7) #define CDC_A_CURRENT_LIMIT (0xf1C0) +#define CDC_A_BYPASS_MODE (0xf1C2) #define CDC_A_BOOST_EN_CTL (0xf1C3) #define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4) #define CDC_A_SEC_ACCESS (0xf1D0) @@ -286,6 +292,11 @@ static const char * const supply_names[] = { #define MBHC_MAX_BUTTONS (5) +struct wcd_reg_seq { + const struct reg_default *seq; + int seq_size; +}; + struct pm8916_wcd_analog_priv { u16 pmic_rev; u16 codec_version; @@ -696,6 +707,59 @@ static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w, return 0; } +static int pm8916_wcd_analog_enable_hphl_pa(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 pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); + + /* This quirk is not required for revisions prior to CAJON_2_0 */ + if (priv->codec_version < 4) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(7000, 7100); + snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, + 0x04, 0x04); + break; + case SND_SOC_DAPM_POST_PMD: + /* wait 20 ms after the digital codec has powered down */ + msleep(20); + snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, + 0x04, 0x00); + break; + } + return 0; +} + +static int pm8916_wcd_analog_enable_hphr_pa(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 pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); + + /* This quirk is not required for revisions prior to CAJON_2_0 */ + if (priv->codec_version < 4) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(7000, 7100); + snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, + 0x04, 0x04); + break; + case SND_SOC_DAPM_POST_PMD: + msleep(20); + snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, + 0x04, 0x00); + break; + } + return 0; +} + static const struct reg_default wcd_reg_defaults_2_0[] = { {CDC_A_RX_COM_OCP_CTL, 0xD1}, {CDC_A_RX_COM_OCP_COUNT, 0xFF}, @@ -715,9 +779,69 @@ static const struct reg_default wcd_reg_defaults_2_0[] = { {CDC_A_MASTER_BIAS_CTL, 0x30}, }; +static const struct wcd_reg_seq pm8916_data = { + .seq = wcd_reg_defaults_2_0, + .seq_size = ARRAY_SIZE(wcd_reg_defaults_2_0), +}; + +static const struct reg_default wcd_reg_defaults_pm8950[] = { + {CDC_A_RX_COM_OCP_CTL, 0xd1}, + {CDC_A_RX_COM_OCP_COUNT, 0xff}, + {CDC_D_SEC_ACCESS, 0xa5}, + {CDC_D_PERPH_RESET_CTL3, 0x0f}, + {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, + {CDC_A_NCP_FBCTRL, 0xa8}, + {CDC_A_NCP_VCTRL, 0xa4}, + {CDC_A_SPKR_DRV_CTL, 0x69}, + {CDC_A_SPKR_DRV_DBG, 0x01}, + {CDC_A_SEC_ACCESS, 0xa5}, + {CDC_A_PERPH_RESET_CTL3, 0x0f}, + {CDC_A_CURRENT_LIMIT, 0x82}, + {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, + {CDC_A_SPKR_DAC_CTL, 0x03}, + {CDC_A_SPKR_OCP_CTL, 0xe1}, + {CDC_A_RX_HPH_BIAS_PA, 0xfa}, + {CDC_A_MASTER_BIAS_CTL, 0x30}, + {CDC_A_MICB_1_INT_RBIAS, 0x00}, +}; + +static const struct wcd_reg_seq pm8950_data = { + .seq = wcd_reg_defaults_pm8950, + .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8950), +}; + +static const struct reg_default wcd_reg_defaults_pm8953[] = { + {CDC_A_RX_COM_OCP_CTL, 0xd1}, + {CDC_A_RX_COM_OCP_COUNT, 0xff}, + {CDC_D_SEC_ACCESS, 0xa5}, + {CDC_D_PERPH_RESET_CTL3, 0x0f}, + {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, + {CDC_A_NCP_FBCTRL, 0xa8}, + {CDC_A_NCP_VCTRL, 0xa4}, + {CDC_A_SPKR_DRV_CTL, 0x69}, + {CDC_A_SPKR_DRV_DBG, 0x01}, + {CDC_A_SEC_ACCESS, 0xa5}, + {CDC_A_PERPH_RESET_CTL3, 0x0f}, + {CDC_A_CURRENT_LIMIT, 0xa2}, + {CDC_A_BYPASS_MODE, 0x18}, + {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, + {CDC_A_SPKR_DAC_CTL, 0x03}, + {CDC_A_SPKR_OCP_CTL, 0xe1}, + {CDC_A_RX_HPH_BIAS_PA, 0xfa}, + {CDC_A_RX_EAR_STATUS, 0x10}, + {CDC_A_MASTER_BIAS_CTL, 0x30}, + {CDC_A_MICB_1_INT_RBIAS, 0x00}, +}; + +static const struct wcd_reg_seq pm8953_data = { + .seq = wcd_reg_defaults_pm8953, + .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8953), +}; + static int pm8916_wcd_analog_probe(struct snd_soc_component *component) { struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); + const struct wcd_reg_seq *wcd_reg_init_data; int err, reg; err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); @@ -738,9 +862,11 @@ static int pm8916_wcd_analog_probe(struct snd_soc_component *component) snd_soc_component_write(component, CDC_D_PERPH_RESET_CTL4, 0x01); snd_soc_component_write(component, CDC_A_PERPH_RESET_CTL4, 0x01); - for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++) - snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg, - wcd_reg_defaults_2_0[reg].def); + wcd_reg_init_data = of_device_get_match_data(component->dev); + + for (reg = 0; reg < wcd_reg_init_data->seq_size; reg++) + snd_soc_component_write(component, wcd_reg_init_data->seq[reg].reg, + wcd_reg_init_data->seq[reg].def); priv->component = component; @@ -883,11 +1009,15 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux), SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0, + pm8916_wcd_analog_enable_hphl_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0, + pm8916_wcd_analog_enable_hphr_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux), SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, 0), @@ -1259,7 +1389,9 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) } static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { - { .compatible = "qcom,pm8916-wcd-analog-codec", }, + { .compatible = "qcom,pm8916-wcd-analog-codec", .data = &pm8916_data }, + { .compatible = "qcom,pm8950-wcd-analog-codec", .data = &pm8950_data }, + { .compatible = "qcom,pm8953-wcd-analog-codec", .data = &pm8953_data }, { } }; diff --git a/sound/soc/codecs/mt6357.c b/sound/soc/codecs/mt6357.c index 674cf7df9df4..3b07087e1cab 100644 --- a/sound/soc/codecs/mt6357.c +++ b/sound/soc/codecs/mt6357.c @@ -1834,8 +1834,8 @@ static int mt6357_platform_driver_probe(struct platform_device *pdev) } static const struct platform_device_id mt6357_platform_ids[] = { - {"mt6357-sound", 0}, - { /* sentinel */ }, + { .name = "mt6357-sound" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6357_platform_ids); diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c index ed34cc15b80e..e1190c644021 100644 --- a/sound/soc/codecs/mt6359-accdet.c +++ b/sound/soc/codecs/mt6359-accdet.c @@ -6,6 +6,7 @@ // Author: Argus Lin <argus.lin@mediatek.com> // +#include <linux/cleanup.h> #include <linux/of.h> #include <linux/input.h> #include <linux/kthread.h> @@ -398,14 +399,13 @@ static void mt6359_accdet_work(struct work_struct *work) struct mt6359_accdet *priv = container_of(work, struct mt6359_accdet, accdet_work); - mutex_lock(&priv->res_lock); + guard(mutex)(&priv->res_lock); priv->pre_accdet_status = priv->accdet_status; check_jack_btn_type(priv); if (priv->jack_plugged && priv->pre_accdet_status != priv->accdet_status) mt6359_accdet_jack_report(priv); - mutex_unlock(&priv->res_lock); } static void mt6359_accdet_jd_work(struct work_struct *work) @@ -416,7 +416,7 @@ static void mt6359_accdet_jd_work(struct work_struct *work) struct mt6359_accdet *priv = container_of(work, struct mt6359_accdet, jd_work); - mutex_lock(&priv->res_lock); + guard(mutex)(&priv->res_lock); if (priv->jd_sts == M_PLUG_IN) { priv->jack_plugged = true; @@ -450,7 +450,6 @@ static void mt6359_accdet_jd_work(struct work_struct *work) if (priv->caps & ACCDET_PMIC_EINT_IRQ) recover_eint_setting(priv); - mutex_unlock(&priv->res_lock); } static irqreturn_t mt6359_accdet_irq(int irq, void *data) @@ -459,7 +458,7 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) unsigned int irq_val = 0, val = 0, value = 0; int ret; - mutex_lock(&priv->res_lock); + guard(mutex)(&priv->res_lock); regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val); if (irq_val & ACCDET_IRQ_MASK_SFT) { @@ -474,7 +473,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) 1000); if (ret) { dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); - mutex_unlock(&priv->res_lock); return IRQ_NONE; } regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, @@ -498,7 +496,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) if (ret) { dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); - mutex_unlock(&priv->res_lock); return IRQ_NONE; } regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, @@ -521,7 +518,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) if (ret) { dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); - mutex_unlock(&priv->res_lock); return IRQ_NONE; } regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, @@ -540,7 +536,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) queue_work(priv->jd_workqueue, &priv->jd_work); } - mutex_unlock(&priv->res_lock); return IRQ_HANDLED; } diff --git a/sound/soc/codecs/ntpfw.c b/sound/soc/codecs/ntpfw.c index 5ced2e966ab7..b6443e24ae8e 100644 --- a/sound/soc/codecs/ntpfw.c +++ b/sound/soc/codecs/ntpfw.c @@ -89,7 +89,7 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) { struct device *dev = &i2c->dev; const struct ntpfw_chunk *chunk; - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; const u8 *data; size_t leftover; int ret; @@ -101,10 +101,8 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) return ret; } - if (!ntpfw_verify(dev, fw->data, fw->size, magic)) { - ret = -EINVAL; - goto done; - } + if (!ntpfw_verify(dev, fw->data, fw->size, magic)) + return -EINVAL; data = fw->data + sizeof(struct ntpfw_header); leftover = fw->size - sizeof(struct ntpfw_header); @@ -112,23 +110,18 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) while (leftover) { chunk = (struct ntpfw_chunk *)data; - if (!ntpfw_verify_chunk(dev, chunk, leftover)) { - ret = -EINVAL; - goto done; - } + if (!ntpfw_verify_chunk(dev, chunk, leftover)) + return -EINVAL; ret = ntpfw_send_chunk(i2c, chunk); if (ret) - goto done; + return ret; data += be16_to_cpu(chunk->length) + sizeof(*chunk); leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk); } -done: - release_firmware(fw); - - return ret; + return 0; } EXPORT_SYMBOL_GPL(ntpfw_load); diff --git a/sound/soc/codecs/ntpfw.h b/sound/soc/codecs/ntpfw.h index 1cf10d5480ee..efbe9aa1eca5 100644 --- a/sound/soc/codecs/ntpfw.h +++ b/sound/soc/codecs/ntpfw.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/** +/* * ntpfw.h - Firmware helper functions for Neofidelity codecs * * Copyright (c) 2024, SaluteDevices. All Rights Reserved. @@ -13,10 +13,11 @@ /** * ntpfw_load - load firmware to amplifier over i2c interface. * - * @i2c Pointer to amplifier's I2C client. - * @name Firmware file name. - * @magic Magic number to validate firmware. - * @return 0 or error code upon error. + * @i2c: Pointer to amplifier's I2C client. + * @name: Firmware file name. + * @magic: Magic number to validate firmware. + * + * Returns: 0 or error code upon error. */ int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic); diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index cb923cecb47f..60fdbe5c4e05 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -199,10 +199,17 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream, return pcm1681_set_deemph(component); } +static const u64 pcm1681_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J; + static const struct snd_soc_dai_ops pcm1681_dai_ops = { .set_fmt = pcm1681_set_dai_fmt, .hw_params = pcm1681_hw_params, .mute_stream = pcm1681_mute, + .auto_selectable_formats = &pcm1681_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c index b68a528000be..bb15da1c24aa 100644 --- a/sound/soc/codecs/pcm1754.c +++ b/sound/soc/codecs/pcm1754.c @@ -78,10 +78,16 @@ static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream) return 0; } +static const u64 pcm1754_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; + static const struct snd_soc_dai_ops pcm1754_dai_ops = { .set_fmt = pcm1754_set_dai_fmt, .hw_params = pcm1754_hw_params, .mute_stream = pcm1754_mute_stream, + .auto_selectable_formats = &pcm1754_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_dai_driver pcm1754_dai = { diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c index 3ab381e9a856..f06e353743aa 100644 --- a/sound/soc/codecs/pcm1789.c +++ b/sound/soc/codecs/pcm1789.c @@ -164,11 +164,18 @@ static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static const u64 pcm1789_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J; + static const struct snd_soc_dai_ops pcm1789_dai_ops = { .set_fmt = pcm1789_set_dai_fmt, .hw_params = pcm1789_hw_params, .mute_stream = pcm1789_mute, .trigger = pcm1789_trigger, + .auto_selectable_formats = &pcm1789_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c index f52ff66b6e64..cb70927872aa 100644 --- a/sound/soc/codecs/pcm179x.c +++ b/sound/soc/codecs/pcm179x.c @@ -142,10 +142,16 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream, return 0; } +static const u64 pcm179x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; + static const struct snd_soc_dai_ops pcm179x_dai_ops = { .set_fmt = pcm179x_set_dai_fmt, .hw_params = pcm179x_hw_params, .mute_stream = pcm179x_mute, + .auto_selectable_formats = &pcm179x_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index 0d1103fe4e04..461ec74e8752 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -473,11 +473,20 @@ static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, return 0; } +static const u64 pcm186x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops pcm186x_dai_ops = { .set_sysclk = pcm186x_set_dai_sysclk, .set_tdm_slot = pcm186x_set_tdm_slot, .set_fmt = pcm186x_set_fmt, .hw_params = pcm186x_hw_params, + .auto_selectable_formats = &pcm186x_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver pcm1863_dai = { diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c index 8974200652e7..f01c46583e89 100644 --- a/sound/soc/codecs/pcm3060.c +++ b/sound/soc/codecs/pcm3060.c @@ -164,10 +164,18 @@ val_ready: return 0; } +static const u64 pcm3060_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops pcm3060_dai_ops = { .set_sysclk = pcm3060_set_sysclk, .set_fmt = pcm3060_set_fmt, .hw_params = pcm3060_hw_params, + .auto_selectable_formats = &pcm3060_selectable_formats, + .num_auto_selectable_formats = 1, }; #define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \ diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index fe3b5011fa16..f3e20919042b 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -6,7 +6,7 @@ * Copyright 2014 Linaro Ltd */ - +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/module.h> #include <linux/clk.h> @@ -78,28 +78,10 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_POWER, 0x00 }, { PCM512x_MUTE, 0x00 }, { PCM512x_DSP, 0x00 }, - { PCM512x_PLL_REF, 0x00 }, - { PCM512x_DAC_REF, 0x00 }, - { PCM512x_DAC_ROUTING, 0x11 }, - { PCM512x_DSP_PROGRAM, 0x01 }, - { PCM512x_CLKDET, 0x00 }, - { PCM512x_AUTO_MUTE, 0x00 }, - { PCM512x_ERROR_DETECT, 0x00 }, - { PCM512x_DIGITAL_VOLUME_1, 0x00 }, - { PCM512x_DIGITAL_VOLUME_2, 0x30 }, - { PCM512x_DIGITAL_VOLUME_3, 0x30 }, - { PCM512x_DIGITAL_MUTE_1, 0x22 }, - { PCM512x_DIGITAL_MUTE_2, 0x00 }, - { PCM512x_DIGITAL_MUTE_3, 0x07 }, - { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, - { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, - { PCM512x_UNDERVOLTAGE_PROT, 0x00 }, - { PCM512x_ANALOG_MUTE_CTRL, 0x00 }, - { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, - { PCM512x_VCOM_CTRL_1, 0x00 }, - { PCM512x_VCOM_CTRL_2, 0x01 }, { PCM512x_BCLK_LRCLK_CFG, 0x00 }, { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_REF, 0x00 }, { PCM512x_GPIO_DACIN, 0x00 }, { PCM512x_GPIO_PLLIN, 0x00 }, { PCM512x_SYNCHRONIZE, 0x10 }, @@ -117,8 +99,26 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_FS_SPEED_MODE, 0x00 }, { PCM512x_IDAC_1, 0x01 }, { PCM512x_IDAC_2, 0x00 }, + { PCM512x_ERROR_DETECT, 0x00 }, { PCM512x_I2S_1, 0x02 }, { PCM512x_I2S_2, 0x00 }, + { PCM512x_DAC_ROUTING, 0x11 }, + { PCM512x_DSP_PROGRAM, 0x01 }, + { PCM512x_CLKDET, 0x00 }, + { PCM512x_AUTO_MUTE, 0x00 }, + { PCM512x_DIGITAL_VOLUME_1, 0x00 }, + { PCM512x_DIGITAL_VOLUME_2, 0x30 }, + { PCM512x_DIGITAL_VOLUME_3, 0x30 }, + { PCM512x_DIGITAL_MUTE_1, 0x22 }, + { PCM512x_DIGITAL_MUTE_2, 0x00 }, + { PCM512x_DIGITAL_MUTE_3, 0x07 }, + { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, + { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, + { PCM512x_UNDERVOLTAGE_PROT, 0x00 }, + { PCM512x_ANALOG_MUTE_CTRL, 0x00 }, + { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, + { PCM512x_VCOM_CTRL_1, 0x00 }, + { PCM512x_VCOM_CTRL_2, 0x01 }, }; static bool pcm512x_readable(struct device *dev, unsigned int reg) @@ -399,10 +399,9 @@ static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - mutex_lock(&pcm512x->mutex); + guard(mutex)(&pcm512x->mutex); ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4); ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2); - mutex_unlock(&pcm512x->mutex); return 0; } @@ -414,7 +413,7 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); int ret, changed = 0; - mutex_lock(&pcm512x->mutex); + guard(mutex)(&pcm512x->mutex); if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) { pcm512x->mute ^= 0x4; @@ -430,13 +429,10 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, if (ret != 0) { dev_err(component->dev, "Failed to update digital mute: %d\n", ret); - mutex_unlock(&pcm512x->mutex); return ret; } } - mutex_unlock(&pcm512x->mutex); - return changed; } @@ -1465,7 +1461,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) int ret; unsigned int mute_det; - mutex_lock(&pcm512x->mutex); + guard(mutex)(&pcm512x->mutex); if (mute) { pcm512x->mute |= 0x1; @@ -1475,7 +1471,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) if (ret != 0) { dev_err(component->dev, "Failed to set digital mute: %d\n", ret); - goto unlock; + return ret; } regmap_read_poll_timeout(pcm512x->regmap, @@ -1488,7 +1484,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) if (ret != 0) { dev_err(component->dev, "Failed to update digital mute: %d\n", ret); - goto unlock; + return ret; } regmap_read_poll_timeout(pcm512x->regmap, @@ -1499,18 +1495,24 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) 200, 10000); } -unlock: - mutex_unlock(&pcm512x->mutex); - return ret; } +static const u64 pcm512x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B; + static const struct snd_soc_dai_ops pcm512x_dai_ops = { .startup = pcm512x_dai_startup, .hw_params = pcm512x_hw_params, .set_fmt = pcm512x_set_fmt, .mute_stream = pcm512x_mute, .set_bclk_ratio = pcm512x_set_bclk_ratio, + .auto_selectable_formats = &pcm512x_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index 4ac4448ac3c8..db85ae2f8aed 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -12,6 +12,7 @@ // Author: Shenghao Ding <shenghao-ding@ti.com> // +#include <linux/cleanup.h> #include <linux/unaligned.h> #include <linux/firmware.h> #include <linux/gpio/consumer.h> @@ -605,7 +606,7 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, unsigned int reg = mc->reg; unsigned int val; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); if (pcm_dev->chip_id == PCM1690) { ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL, @@ -613,18 +614,18 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, if (ret) { dev_err(pcm_dev->dev, "%s: read mode err=%d\n", __func__, ret); - goto out; + return ret; } val &= PCM1690_REG_MODE_CTRL_DAMS_MSK; /* Set to wide-range mode, before using vol ctrl. */ if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) { ucontrol->value.integer.value[0] = -25500; - goto out; + return ret; } /* Set to fine mode, before using fine vol ctrl. */ if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) { ucontrol->value.integer.value[0] = -12750; - goto out; + return ret; } } @@ -632,15 +633,14 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, if (ret) { dev_err(pcm_dev->dev, "%s: read err=%d\n", __func__, ret); - goto out; + return ret; } val = (val >> shift) & mask; val = (val > max) ? max : val; val = mc->invert ? max - val : val; ucontrol->value.integer.value[0] = val; -out: - mutex_unlock(&pcm_dev->codec_lock); + return ret; } @@ -678,7 +678,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val, val_mask; unsigned int reg = mc->reg; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); val = ucontrol->value.integer.value[0] & mask; val = (val > max) ? max : val; val = mc->invert ? max - val : val; @@ -702,7 +702,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, __func__, rc); else rc = 1; - mutex_unlock(&pcm_dev->codec_lock); + return rc; } @@ -1577,10 +1577,10 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) { struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp); struct i2c_adapter *adap = pcm_dev->client->adapter; - const struct firmware *fw_entry = NULL; + const struct firmware *fw_entry __free(firmware) = NULL; int ret, i, j; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); pcm_dev->component = comp; @@ -1588,7 +1588,7 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) for (j = 0; j < 2; j++) { ret = pcmdev_gain_ctrl_add(pcm_dev, i, j); if (ret < 0) - goto out; + return ret; } } @@ -1621,21 +1621,17 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) if (ret) { dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__, pcm_dev->bin_name, ret); - goto out; + return ret; } ret = pcmdev_regbin_ready(fw_entry, pcm_dev); if (ret) { dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__, pcm_dev->bin_name, ret); - goto out; + return ret; } - ret = pcmdev_profile_ctrl_add(pcm_dev); -out: - release_firmware(fw_entry); - mutex_unlock(&pcm_dev->codec_lock); - return ret; + return pcmdev_profile_ctrl_add(pcm_dev); } @@ -1645,9 +1641,8 @@ static void pcmdevice_comp_remove(struct snd_soc_component *codec) if (!pcm_dev) return; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); pcmdevice_config_info_remove(pcm_dev); - mutex_unlock(&pcm_dev->codec_lock); } static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = { @@ -1890,9 +1885,9 @@ static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream) else block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type); - mutex_unlock(&pcm_dev->codec_lock); + return 0; } diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index 2d71d204d8fa..f1ded68c2d75 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -6,6 +6,7 @@ // // Author: Herve Codina <herve.codina@bootlin.com> +#include <linux/cleanup.h> #include <linux/unaligned.h> #include <linux/clk.h> #include <linux/firmware.h> @@ -1537,17 +1538,14 @@ static int peb2466_fw_parse(struct snd_soc_component *component, static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int ret; ret = request_firmware(&fw, fw_name, component->dev); if (ret) return ret; - ret = peb2466_fw_parse(component, fw->data, fw->size); - release_firmware(fw); - - return ret; + return peb2466_fw_parse(component, fw->data, fw->size); } static int peb2466_component_probe(struct snd_soc_component *component) @@ -1704,13 +1702,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x * So, a specific cache value is used. */ - mutex_lock(&peb2466->gpio.lock); + guard(mutex)(&peb2466->gpio.lock); cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); - if (!cache) { - ret = -EINVAL; - goto end; - } + if (!cache) + return -EINVAL; tmp = *cache; tmp &= ~mask; @@ -1718,14 +1714,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x ret = regmap_write(peb2466->regmap, xr_reg, tmp); if (ret) - goto end; + return ret; *cache = tmp; - ret = 0; -end: - mutex_unlock(&peb2466->gpio.lock); - return ret; + return 0; } static int peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c index 1c612ae4a4b2..182ff396301a 100644 --- a/sound/soc/codecs/pm4125-sdw.c +++ b/sound/soc/codecs/pm4125-sdw.c @@ -126,8 +126,8 @@ static const struct reg_default pm4125_defaults[] = { { PM4125_ANA_HPHPA_FSM_CLK, 0x12 }, { PM4125_ANA_HPHPA_L_GAIN, 0x00 }, { PM4125_ANA_HPHPA_R_GAIN, 0x00 }, - { PM4125_SWR_HPHPA_HD2, 0x1B }, { PM4125_ANA_HPHPA_SPARE_CTL, 0x02 }, + { PM4125_SWR_HPHPA_HD2, 0x1B }, { PM4125_ANA_SURGE_EN, 0x38 }, { PM4125_ANA_COMBOPA_CTL, 0x35 }, { PM4125_ANA_COMBOPA_CTL_4, 0x84 }, @@ -464,10 +464,16 @@ static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev) static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev) { struct pm4125_sdw_priv *priv = dev_get_drvdata(dev); + int ret; if (priv->regmap) { regcache_cache_only(priv->regmap, false); - regcache_sync(priv->regmap); + ret = regcache_sync(priv->regmap); + if (ret) { + regcache_cache_only(priv->regmap, true); + regcache_mark_dirty(priv->regmap); + return ret; + } } return 0; diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c index 29655175ea28..674a2fbc4eda 100644 --- a/sound/soc/codecs/pm4125.c +++ b/sound/soc/codecs/pm4125.c @@ -5,6 +5,7 @@ #include <linux/component.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @@ -1295,7 +1296,7 @@ static const struct irq_domain_ops pm4125_domain_ops = { static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev) { - pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL); + pm4125->virq = irq_domain_create_linear(NULL, 1, &pm4125_domain_ops, NULL); if (!(pm4125->virq)) { dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); return -EINVAL; diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 95405cea8143..caf75e5657ef 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -784,7 +784,12 @@ static int rt1017_sdca_dev_resume(struct device *dev) } regcache_cache_only(rt1017->regmap, false); - regcache_sync(rt1017->regmap); + ret = regcache_sync(rt1017->regmap); + if (ret) { + regcache_cache_only(rt1017->regmap, true); + regcache_mark_dirty(rt1017->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/rt1017-sdca-sdw.h b/sound/soc/codecs/rt1017-sdca-sdw.h index 4932b5dbe3c0..1604e1cfd85e 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.h +++ b/sound/soc/codecs/rt1017-sdca-sdw.h @@ -165,19 +165,19 @@ static const struct reg_default rt1017_sdca_reg_defaults[] = { { 0xdb09, 0x0f }, { 0xdb0a, 0xff }, { 0xdb14, 0x00 }, - - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, - RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, RT1017_SDCA_CTL_FU_MUTE, 0x01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22, RT1017_SDCA_CTL_BYPASS, 0), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, - RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22, RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 }, }; #endif /* __RT1017_SDW_H__ */ diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index ca318dbd946e..2ef47a82c984 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -59,13 +59,13 @@ static const struct reg_default rt1316_reg_defaults[] = { { 0xd101, 0x00 }, { 0xd102, 0x30 }, { 0xd103, 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, }; static const struct reg_sequence rt1316_blind_write[] = { @@ -756,7 +756,12 @@ static int rt1316_dev_resume(struct device *dev) } regcache_cache_only(rt1316->regmap, false); - regcache_sync(rt1316->regmap); + ret = regcache_sync(rt1316->regmap); + if (ret) { + regcache_cache_only(rt1316->regmap, true); + regcache_mark_dirty(rt1316->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index c038ac0e3b76..efadb6b1b82d 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -235,10 +235,10 @@ static const struct reg_default rt1318_reg_defaults[] = { { 0xf805, 0x00 }, { 0xf806, 0x07 }, { 0xf807, 0xff }, - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, RT1318_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, }; @@ -830,7 +830,12 @@ static int rt1318_dev_resume(struct device *dev) return ret; regcache_cache_only(rt1318->regmap, false); - regcache_sync(rt1318->regmap); + ret = regcache_sync(rt1318->regmap); + if (ret) { + regcache_cache_only(rt1318->regmap, true); + regcache_mark_dirty(rt1318->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c index d13fd0e14125..55607f1b9a1e 100644 --- a/sound/soc/codecs/rt1318.c +++ b/sound/soc/codecs/rt1318.c @@ -337,6 +337,8 @@ static const struct reg_default rt1318_reg[] = { { 0xdd08, 0x40 }, { 0xdd12, 0x00 }, { 0xdd35, 0x00 }, + { 0xdd93, 0x00 }, + { 0xdd94, 0x64 }, { 0xddb5, 0x00 }, { 0xddb6, 0x40 }, { 0xddb7, 0x00 }, @@ -345,8 +347,6 @@ static const struct reg_default rt1318_reg[] = { { 0xddc6, 0x00 }, { 0xddc7, 0x00 }, { 0xddc8, 0x00 }, - { 0xdd93, 0x00 }, - { 0xdd94, 0x64 }, { 0xdf00, 0x00 }, { 0xdf5f, 0x00 }, { 0xdf60, 0x00 }, diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 42acb6cef77a..90333779971d 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -410,6 +410,402 @@ static const struct reg_sequence rt1321_blind_write[] = { { 0x41001988, 0x03 }, }; +static const struct reg_sequence rt1321_va1_blind_write[] = { + { 0x0000c003, 0xf0 }, + { 0x0000c01b, 0xfc }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c2, 0x00 }, + { 0x0000c5c1, 0x10 }, + { 0x0000c5c0, 0x04 }, + { 0x0000c5c7, 0x03 }, + { 0x0000c5c6, 0x10 }, + { 0x0000c526, 0x47 }, + { 0x0000c5c4, 0x12 }, + { 0x0000c5c5, 0x60 }, + { 0x0000c520, 0x10 }, + { 0x0000c521, 0x32 }, + { 0x0000c5c7, 0x00 }, + { 0x0000c5c8, 0x03 }, + { 0x0000c5d3, 0x08 }, + { 0x0000c5d2, 0x0a }, + { 0x0000c5d1, 0x49 }, + { 0x0000c5d0, 0x0f }, + { 0x0000c580, 0x10 }, + { 0x0000c581, 0x32 }, + { 0x0000c582, 0x01 }, + { 0x0000c682, 0x60 }, + { 0x0000c019, 0x10 }, + { 0x0000c5f0, 0x01 }, + { 0x0000c5f7, 0x22 }, + { 0x0000c5f6, 0x22 }, + { 0x0000c057, 0x51 }, + { 0x0000c054, 0x55 }, + { 0x0000c053, 0x55 }, + { 0x0000c052, 0x55 }, + { 0x0000c051, 0x01 }, + { 0x0000c050, 0x15 }, + { 0x0000c060, 0x99 }, + { 0x0000c030, 0x55 }, + { 0x0000c061, 0x55 }, + { 0x0000c063, 0x55 }, + { 0x0000c065, 0xa5 }, + { 0x0000c06b, 0x0a }, + { 0x0000ca05, 0xd6 }, + { 0x0000ca06, 0x11 }, + { 0x0000ca07, 0x1e }, + { 0x0000ca25, 0xd6 }, + { 0x0000ca26, 0x11 }, + { 0x0000ca27, 0x1e }, + { 0x0000cd00, 0x05 }, + { 0x0000cd81, 0x49 }, + { 0x0000cd82, 0x49 }, + { 0x0000c604, 0x40 }, + { 0x0000c609, 0x40 }, + { 0x0000c046, 0xf7 }, + { 0x0000c045, 0xff }, + { 0x0000c044, 0xff }, + { 0x0000c043, 0xff }, + { 0x0000c042, 0xff }, + { 0x0000c041, 0xff }, + { 0x0000c040, 0xff }, + { 0x0000c049, 0xff }, + { 0x0000c028, 0x3f }, + { 0x0000c020, 0x3f }, + { 0x0000c032, 0x13 }, + { 0x0000c033, 0x01 }, + { 0x0000cc10, 0x01 }, + { 0x0000dc20, 0x03 }, + { 0x0000de03, 0x05 }, + { 0x0000dc00, 0x00 }, + { 0x0000c700, 0xf0 }, + { 0x0000c701, 0x13 }, + { 0x0000c900, 0xc3 }, + { 0x0000c570, 0x08 }, + { 0x0000c086, 0x02 }, + { 0x0000c085, 0x7f }, + { 0x0000c084, 0x00 }, + { 0x0000c081, 0xff }, + { 0x0000f084, 0x0f }, + { 0x0000f083, 0xff }, + { 0x0000f082, 0xff }, + { 0x0000f081, 0xff }, + { 0x0000f080, 0xff }, + { 0x20003003, 0x3f }, + { 0x20005818, 0x81 }, + { 0x20009018, 0x81 }, + { 0x2000301c, 0x81 }, + { 0x0000c003, 0xc0 }, + { 0x0000c047, 0x80 }, + { 0x0000d541, 0x80 }, + { 0x0000d487, 0x0b }, + { 0x0000d487, 0x3b }, + { 0x0000d486, 0xc3 }, + { 0x0000d470, 0x89 }, + { 0x0000d471, 0x3a }, + { 0x0000d472, 0x3d }, + { 0x0000d474, 0x11 }, + { 0x0000d475, 0x32 }, + { 0x0000d476, 0x64 }, + { 0x0000d477, 0x10 }, + { 0x0000d478, 0xff }, + { 0x0000d479, 0x20 }, + { 0x0000d47a, 0x10 }, + { 0x0000d73c, 0xb7 }, + { 0x0000d73d, 0xd7 }, + { 0x0000d73e, 0x00 }, + { 0x0000d73f, 0x10 }, + { 0x1000cd56, 0x00 }, + { 0x3fc2dfc0, 0x03 }, + { 0x3fc2dfc1, 0x00 }, + { 0x3fc2dfc2, 0x00 }, + { 0x3fc2dfc3, 0x00 }, + { 0x3fc2dfc4, 0x01 }, + { 0x3fc2dfc5, 0x00 }, + { 0x3fc2dfc6, 0x00 }, + { 0x3fc2dfc7, 0x00 }, + { 0x3fc2df80, 0x00 }, + { 0x3fc2df81, 0x00 }, + { 0x3fc2df82, 0x00 }, + { 0x3fc2df83, 0x00 }, + { 0x0000d541, 0x40 }, + { 0x0000d486, 0x43 }, + { 0x1000db00, 0x04 }, + { 0x1000db01, 0x00 }, + { 0x1000db02, 0x10 }, + { 0x1000db03, 0x00 }, + { 0x1000db04, 0x00 }, + { 0x1000db05, 0x45 }, + { 0x1000db06, 0x0d }, + { 0x1000db07, 0x01 }, + { 0x1000db08, 0x00 }, + { 0x1000db09, 0x00 }, + { 0x1000db0a, 0xbf }, + { 0x1000db0b, 0x0b }, + { 0x1000db0c, 0x11 }, + { 0x1000db0d, 0x00 }, + { 0x1000db0e, 0x00 }, + { 0x1000db0f, 0x00 }, + { 0x1000db10, 0x2c }, + { 0x1000db11, 0xfa }, + { 0x1000db12, 0x00 }, + { 0x1000db13, 0x00 }, + { 0x1000db14, 0x09 }, + { 0x0000d540, 0x21 }, + { 0x0000c570, 0x08 }, + { 0x0000d714, 0x17 }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c8, 0x03 }, + { 0x20009012, 0x00 }, + { 0x0000dd08, 0x17 }, + { 0x0000dd09, 0x0e }, + { 0x0000dd0a, 0x17 }, + { 0x0000dd0b, 0x0e }, + { 0x0000c570, 0x08 }, + { 0x0000d471, 0x3a }, + { 0x0000db00, 0x00 }, + { 0x0000db01, 0x00 }, + { 0x0000db02, 0x73 }, + { 0x0000db03, 0x00 }, + { 0x0000db04, 0x00 }, + { 0x0000db05, 0x00 }, + { 0x0000db06, 0x00 }, + { 0x0000db07, 0x00 }, + { 0x0000db08, 0x7f }, + { 0x0000db09, 0x00 }, + { 0x0000db1a, 0x00 }, + { 0x0000db1b, 0x00 }, + { 0x0000db19, 0x00 }, +}; + +static const struct reg_sequence rt1321_va2_blind_write[] = { + { 0x0000c003, 0xf0 }, + { 0x0000c01b, 0xfc }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c2, 0x00 }, + { 0x0000c5c1, 0x10 }, + { 0x0000c5c0, 0x04 }, + { 0x0000c5c7, 0x03 }, + { 0x0000c5c6, 0x10 }, + { 0x0000c526, 0x47 }, + { 0x0000c5c4, 0x12 }, + { 0x0000c5c5, 0x60 }, + { 0x0000c520, 0x10 }, + { 0x0000c521, 0x32 }, + { 0x0000c5c7, 0x00 }, + { 0x0000c5c8, 0x03 }, + { 0x0000c5d3, 0x08 }, + { 0x0000c5d2, 0x0a }, + { 0x0000c5d1, 0x49 }, + { 0x0000c5d0, 0x0f }, + { 0x0000c580, 0x10 }, + { 0x0000c581, 0x32 }, + { 0x0000c582, 0x01 }, + { 0x0000c682, 0x60 }, + { 0x0000c019, 0x10 }, + { 0x0000c5f0, 0x01 }, + { 0x0000c5f7, 0x22 }, + { 0x0000c5f6, 0x22 }, + { 0x0000c057, 0x51 }, + { 0x0000c054, 0x55 }, + { 0x0000c053, 0x55 }, + { 0x0000c052, 0x55 }, + { 0x0000c051, 0x01 }, + { 0x0000c050, 0x15 }, + { 0x0000c060, 0x99 }, + { 0x0000c030, 0x55 }, + { 0x0000c061, 0x55 }, + { 0x0000c063, 0x55 }, + { 0x0000c065, 0xa5 }, + { 0x0000c06b, 0x0a }, + { 0x0000ca05, 0xd6 }, + { 0x0000ca06, 0x11 }, + { 0x0000ca07, 0x1e }, + { 0x0000ca25, 0xd6 }, + { 0x0000ca26, 0x11 }, + { 0x0000ca27, 0x1e }, + { 0x0000cd00, 0x05 }, + { 0x0000cd81, 0x49 }, + { 0x0000cd82, 0x49 }, + { 0x0000c604, 0x40 }, + { 0x0000c609, 0x40 }, + { 0x0000c046, 0xf7 }, + { 0x0000c045, 0xff }, + { 0x0000c044, 0xff }, + { 0x0000c043, 0xff }, + { 0x0000c042, 0xff }, + { 0x0000c041, 0xff }, + { 0x0000c040, 0xff }, + { 0x0000c049, 0xff }, + { 0x0000c028, 0x3f }, + { 0x0000c020, 0x3f }, + { 0x0000c032, 0x13 }, + { 0x0000c033, 0x01 }, + { 0x0000cc10, 0x01 }, + { 0x0000dc20, 0x03 }, + { 0x0000de03, 0x05 }, + { 0x0000dc00, 0x00 }, + { 0x0000c700, 0xf0 }, + { 0x0000c701, 0x13 }, + { 0x0000c900, 0xc3 }, + { 0x0000c570, 0x08 }, + { 0x0000c086, 0x02 }, + { 0x0000c085, 0x7f }, + { 0x0000c084, 0x00 }, + { 0x0000c081, 0xff }, + { 0x0000f084, 0x0f }, + { 0x0000f083, 0xff }, + { 0x0000f082, 0xff }, + { 0x0000f081, 0xff }, + { 0x0000f080, 0xff }, + { 0x20003003, 0x3f }, + { 0x20005818, 0x81 }, + { 0x20009018, 0x81 }, + { 0x2000301c, 0x81 }, + { 0x0000c003, 0xc0 }, + { 0x0000c047, 0x80 }, + { 0x0000d541, 0x80 }, + { 0x0000d487, 0x0b }, + { 0x0000d487, 0x3b }, + { 0x0000d486, 0xc3 }, + { 0x0000d470, 0x89 }, + { 0x0000d471, 0x3a }, + { 0x0000d472, 0x3d }, + { 0x0000d474, 0x11 }, + { 0x0000d475, 0x32 }, + { 0x0000d476, 0x64 }, + { 0x0000d477, 0x10 }, + { 0x0000d478, 0xff }, + { 0x0000d479, 0x20 }, + { 0x0000d47a, 0x10 }, + { 0x10008000, 0x67 }, + { 0x10008001, 0x80 }, + { 0x10008002, 0x00 }, + { 0x10008003, 0x00 }, + { 0x1000cd56, 0x00 }, + { 0x0000d486, 0x43 }, + { 0x3fc2dfc3, 0x00 }, + { 0x3fc2dfc2, 0x00 }, + { 0x3fc2dfc1, 0x00 }, + { 0x3fc2dfc0, 0x03 }, + { 0x3fc2dfc7, 0x00 }, + { 0x3fc2dfc6, 0x00 }, + { 0x3fc2dfc5, 0x00 }, + { 0x3fc2dfc4, 0x01 }, + { 0x3fc2dfa3, 0x00 }, + { 0x3fc2dfa2, 0x00 }, + { 0x3fc2dfa1, 0x00 }, + { 0x3fc2dfa0, 0x00 }, + { 0x3fc2df80, 0x10 }, + { 0x3fc2df81, 0x20 }, + { 0x3fc2df82, 0x00 }, + { 0x3fc2df83, 0x00 }, + { 0x3fc2df84, 0x50 }, + { 0x3fc2df85, 0x19 }, + { 0x3fc2df86, 0x00 }, + { 0x3fc2df87, 0x00 }, + { 0x3fc2df88, 0x52 }, + { 0x3fc2df89, 0x23 }, + { 0x3fc2df8a, 0x00 }, + { 0x3fc2df8b, 0x00 }, + { 0x3fc2df8c, 0xe0 }, + { 0x3fc2df8d, 0x2e }, + { 0x3fc2df8e, 0x00 }, + { 0x3fc2df8f, 0x00 }, + { 0x3fc2df90, 0xe0 }, + { 0x3fc2df91, 0x2e }, + { 0x3fc2df92, 0x00 }, + { 0x3fc2df93, 0x00 }, + { 0x3fc2df94, 0x01 }, + { 0x3fc2df95, 0x08 }, + { 0x3fc2df96, 0x00 }, + { 0x3fc2df97, 0x00 }, + { 0x3fc2df40, 0x80 }, + { 0x3fc2df41, 0xbb }, + { 0x3fc2df42, 0x00 }, + { 0x3fc2df43, 0x00 }, + { 0x3fc2df44, 0xc0 }, + { 0x3fc2df45, 0x99 }, + { 0x3fc2df46, 0x01 }, + { 0x3fc2df47, 0x00 }, + { 0x3fc2df48, 0x00 }, + { 0x3fc2df49, 0x00 }, + { 0x3fc2df4a, 0x00 }, + { 0x3fc2df4b, 0x00 }, + { 0x3fc2df4c, 0x00 }, + { 0x3fc2df4d, 0x00 }, + { 0x3fc2df4e, 0x00 }, + { 0x3fc2df4f, 0x00 }, + { 0x3fc2df50, 0x01 }, + { 0x3fc2df51, 0x00 }, + { 0x3fc2df52, 0x00 }, + { 0x3fc2df53, 0x00 }, + { 0x3fc2df54, 0x01 }, + { 0x3fc2df55, 0x00 }, + { 0x3fc2df56, 0x00 }, + { 0x3fc2df57, 0x00 }, + { 0x3fc2df58, 0x00 }, + { 0x3fc2df59, 0x00 }, + { 0x3fc2df5a, 0x00 }, + { 0x3fc2df5b, 0x00 }, + { 0x3fc2df5c, 0x01 }, + { 0x3fc2df5d, 0x00 }, + { 0x3fc2df5e, 0x00 }, + { 0x3fc2df5f, 0x00 }, + { 0x3fc2df60, 0x00 }, + { 0x3fc2df61, 0x00 }, + { 0x3fc2df62, 0x00 }, + { 0x3fc2df63, 0x00 }, + { 0x3fc2df64, 0x00 }, + { 0x3fc2df65, 0x00 }, + { 0x3fc2df66, 0x00 }, + { 0x3fc2df67, 0x10 }, + { 0x3fc2df68, 0x01 }, + { 0x3fc2df69, 0x00 }, + { 0x3fc2df6a, 0x00 }, + { 0x3fc2df6b, 0x00 }, + { 0x3fc2df6c, 0x01 }, + { 0x3fc2df6d, 0x00 }, + { 0x3fc2df6e, 0x00 }, + { 0x3fc2df6f, 0x00 }, + { 0x3fc2df70, 0x04 }, + { 0x3fc2df71, 0x00 }, + { 0x3fc2df72, 0x00 }, + { 0x3fc2df73, 0x00 }, + { 0x3fc2df74, 0x01 }, + { 0x3fc2df75, 0x00 }, + { 0x3fc2df76, 0x00 }, + { 0x3fc2df77, 0x00 }, + { 0x1000db00, 0x04 }, + { 0x1000db01, 0x00 }, + { 0x1000db02, 0x10 }, + { 0x1000db03, 0x00 }, + { 0x1000db04, 0x00 }, + { 0x1000db05, 0x45 }, + { 0x1000db06, 0x0d }, + { 0x1000db07, 0x01 }, + { 0x1000db08, 0x00 }, + { 0x1000db09, 0x00 }, + { 0x1000db0a, 0xbf }, + { 0x1000db0b, 0x0b }, + { 0x1000db0c, 0x11 }, + { 0x1000db0d, 0x00 }, + { 0x1000db0e, 0x00 }, + { 0x1000db0f, 0x00 }, + { 0x1000db10, 0x2c }, + { 0x1000db11, 0xfa }, + { 0x1000db12, 0x00 }, + { 0x1000db13, 0x00 }, + { 0x1000db14, 0x09 }, + { 0x0000d540, 0x21 }, + { 0x0000d714, 0x17 }, + { 0x0000dd0b, 0x0d }, + { 0x0000dd0a, 0xff }, + { 0x0000dd09, 0x0d }, + { 0x0000dd08, 0xff }, + { 0x0000c5fb, 0x12 }, + { 0x0000c570, 0x08 }, +}; + static const struct reg_default rt1320_reg_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, @@ -440,6 +836,7 @@ static const struct reg_default rt1320_mbq_defaults[] = { static bool rt1320_readable_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x004d: case 0xc000 ... 0xc086: case 0xc400 ... 0xc409: case 0xc480 ... 0xc48f: @@ -497,10 +894,15 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER: case 0x1000f008: case 0x1000f021: - case 0x2000300f: + case 0x20003000 ... 0x2000300f: case 0x2000301c: - case 0x2000900f: + case 0x20003040 ... 0x20003059: + case 0x20005800 ... 0x2000580f: + case 0x20005818: + case 0x20005840 ... 0x20005859: + case 0x20009000 ... 0x2000900f: case 0x20009018: + case 0x20009040 ... 0x20009059: case 0x3fc000c0 ... 0x3fc2dfc8: case 0x3fe00000 ... 0x3fe36fff: /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */ @@ -545,6 +947,7 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) static bool rt1320_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x004d: case 0xc000: case 0xc003: case 0xc081: @@ -596,15 +999,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) case 0x1000c000 ... 0x1000dfff: case 0x1000f008: case 0x1000f021: - case 0x2000300f: + case 0x2000300e ... 0x2000300f: case 0x2000301c: - case 0x2000900f: + case 0x20003040 ... 0x20003059: + case 0x2000580e ... 0x2000580f: + case 0x20005818: + case 0x20005840 ... 0x20005859: + case 0x2000900e ... 0x2000900f: case 0x20009018: + case 0x20009040 ... 0x20009059: case 0x3fc2ab80 ... 0x3fc2ac4c: case 0x3fc2b780: case 0x3fc2bf80 ... 0x3fc2bf83: case 0x3fc2bfc0 ... 0x3fc2bfc8: - case 0x3fc2d300 ... 0x3fc2d354: + case 0x3fc2d300 ... 0x3fc2d3cc: case 0x3fc2dfc0 ... 0x3fc2dfc8: case 0x3fe2e000 ... 0x3fe2e003: case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0): @@ -892,6 +1300,42 @@ static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320) return 0; } +static int rt1320_dspfw_status(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + unsigned int fw_status_addr, fw_ready; + unsigned int dspfw_run; + + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + fw_status_addr = RT1320_DSPFW_STATUS_ADDR; + break; + case RT1321_DEV_ID: + fw_status_addr = RT1321_DSPFW_STATUS_ADDR; + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } + + regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); + fw_ready &= 0x1; + + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_read(rt1320->regmap, 0xf01e, &dspfw_run); + dspfw_run &= 0x1; + fw_ready = (!dspfw_run && fw_ready); + } + + if (fw_ready) { + dev_dbg(dev, "%s, DSP FW was already\n", __func__); + return 1; + } + + dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); + return 0; +} + static int rt1320_check_power_state_ready(struct rt1320_sdw_priv *rt1320, enum rt1320_power_state ps) { struct device *dev = &rt1320->sdw_slave->dev; @@ -982,6 +1426,9 @@ static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_ if (!tempbuf) return -ENOMEM; + if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) + paramid += 0x0bff0000; + paramhr.moudleid = 1; paramhr.commandtype = cmdid; /* 8 is "sizeof(paramid) + sizeof(paramlength)" */ @@ -1120,45 +1567,73 @@ static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) l_calir0, l_calir0_lo, r_calir0, r_calir0_lo); } +static int rt1320_pilot_tone_output(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + int l_targetpostgain, r_targetpostgain; + unsigned long long factor = (1 << 12); + int l_pilotgain[9], r_pilotgain[9]; + const int postgain_step = 234; + int targetGain; + + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + targetGain = -320000; + break; + case RT1321_DEV_ID: + targetGain = -420000; + if (rt1320->version_id == RT1321_VA0) + targetGain = -320000; + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } + + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 70, &l_pilotgain[0], sizeof(l_pilotgain)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 71, &r_pilotgain[0], sizeof(r_pilotgain)); + dev_dbg(dev, "%s, LR pilotgain %d, %d\n", __func__, l_pilotgain[2], r_pilotgain[2]); + + /* calculate pilot tone gain */ + l_pilotgain[2] = (l_pilotgain[2] * 10000) / factor; + r_pilotgain[2] = (r_pilotgain[2] * 10000) / factor; + + /* calculate post gain to meet target gain */ + l_targetpostgain = abs(targetGain - l_pilotgain[2]) / postgain_step; + r_targetpostgain = abs(targetGain - r_pilotgain[2]) / postgain_step; + l_targetpostgain = 0xfff - l_targetpostgain; + r_targetpostgain = 0xfff - r_targetpostgain; + dev_dbg(dev, "%s, LR targetpostgain=0x%x, 0x%x\n", __func__, l_targetpostgain, r_targetpostgain); + + regmap_write(rt1320->regmap, 0xdd0b, (l_targetpostgain & 0xf00) >> 8); + regmap_write(rt1320->regmap, 0xdd0a, l_targetpostgain & 0xff); + regmap_write(rt1320->regmap, 0xdd09, (r_targetpostgain & 0xf00) >> 8); + regmap_write(rt1320->regmap, 0xdd08, r_targetpostgain & 0xff); + + return 0; +} + static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) { struct device *dev = &rt1320->sdw_slave->dev; struct rt1320_datafixpoint audfixpoint[2]; unsigned int reg_c5fb, reg_c570, reg_cd00; - unsigned int vol_reg[4], fw_ready; + unsigned int vol_reg[4]; unsigned long long l_meanr0, r_meanr0; - unsigned int fw_status_addr; int l_re[5], r_re[5]; int ret, tmp; unsigned long long factor = (1 << 27); unsigned short l_advancegain, r_advancegain; unsigned int delay_s = 7; /* delay seconds for the calibration */ + int dspfw_status; if (!rt1320->component) return; - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return; - } - - /* set volume 0dB */ regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]); regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]); regmap_read(rt1320->regmap, 0xdd09, &vol_reg[1]); regmap_read(rt1320->regmap, 0xdd08, &vol_reg[0]); - regmap_write(rt1320->regmap, 0xdd0b, 0x0f); - regmap_write(rt1320->regmap, 0xdd0a, 0xff); - regmap_write(rt1320->regmap, 0xdd09, 0x0f); - regmap_write(rt1320->regmap, 0xdd08, 0xff); - regmap_read(rt1320->regmap, 0xc5fb, ®_c5fb); regmap_read(rt1320->regmap, 0xc570, ®_c570); regmap_read(rt1320->regmap, 0xcd00, ®_cd00); @@ -1171,9 +1646,8 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) goto _finish_; } - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (!fw_ready) { + dspfw_status = rt1320_dspfw_status(rt1320); + if (dspfw_status <= 0) { dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); goto _finish_; } @@ -1184,8 +1658,19 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) goto _finish_; } - if (rt1320->dev_id == RT1320_DEV_ID) - regmap_write(rt1320->regmap, 0xc5fb, 0x00); + /* fine tune pilot tone output */ + ret = rt1320_pilot_tone_output(rt1320); + if (ret < 0) { + dev_dbg(dev, "%s, Failed to tune pilot tone output\n", __func__); + goto _finish_; + } + + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); + regmap_write(rt1320->regmap, 0xc5c4, 0x12); + } + + regmap_write(rt1320->regmap, 0xc5fb, 0x00); regmap_write(rt1320->regmap, 0xc570, 0x0b); regmap_write(rt1320->regmap, 0xcd00, 0xc5); @@ -1246,6 +1731,11 @@ _finish_: SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03); rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03); + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); + regmap_write(rt1320->regmap, 0xc5c4, 0x10); + } + /* advance gain will be set when R0 load, not here */ regmap_write(rt1320->regmap, 0xdd0b, vol_reg[3]); regmap_write(rt1320->regmap, 0xdd0a, vol_reg[2]); @@ -1296,7 +1786,7 @@ static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol, static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) { struct sdw_slave *slave = rt1320->sdw_slave; - const struct firmware *patch; + const struct firmware *patch __free(firmware) = NULL; const char *filename; unsigned int addr, val, min_addr, max_addr; const unsigned char *ptr; @@ -1312,6 +1802,9 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) max_addr = 0x10007fff; break; case RT1321_DEV_ID: + if (rt1320->version_id == RT1321_VA2) + return; + filename = RT1321_VA_MCU_PATCH; min_addr = 0x10008000; max_addr = 0x10008fff; @@ -1347,17 +1840,15 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) if (addr > max_addr || addr < min_addr) { dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr); - goto _exit_; + return; } if (val > 0xff) { dev_err(&slave->dev, "%s: the value 0x%x is wrong", __func__, val); - goto _exit_; + return; } regmap_write(rt1320->regmap, addr, val); } } -_exit_: - release_firmware(patch); } } @@ -1382,30 +1873,17 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320) static void rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, unsigned int r_t0) { struct device *dev = &rt1320->sdw_slave->dev; - unsigned int factor = (1 << 22), fw_ready; + unsigned int factor = (1 << 22); int l_t0_data[38], r_t0_data[38]; - unsigned int fw_status_addr; - - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return; - } + int dspfw_status; regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (!fw_ready) { + dspfw_status = rt1320_dspfw_status(rt1320); + if (dspfw_status <= 0) { dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__); goto _exit_; } @@ -1444,7 +1922,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) struct device *dev = &rt1320->sdw_slave->dev; static const char func_tag[] = "FUNC"; static const char xu_tag[] = "XU"; - const struct firmware *rae_fw = NULL; + const struct firmware *rae_fw __free(firmware) = NULL; unsigned int fw_offset; unsigned char *fw_data; unsigned char *param_data; @@ -1483,22 +1961,46 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) request_firmware(&rae_fw, rae_filename, dev); if (rae_fw) { - /* RAE CRC clear */ - regmap_write(rt1320->regmap, 0xe80b, 0x0f); - - /* RAE stop & CRC disable */ - regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); - - while (--retry) { - regmap_read(rt1320->regmap, 0xe83f, &value); - if (value & 0x40) - break; - usleep_range(1000, 1100); - } - if (!retry && !(value & 0x40)) { - dev_err(dev, "%s: RAE is not ready to load\n", __func__); - release_firmware(rae_fw); - return -ETIMEDOUT; + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + /* RAE CRC clear */ + regmap_write(rt1320->regmap, 0xe80b, 0x0f); + /* RAE stop & CRC disable */ + regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); + while (--retry) { + regmap_read(rt1320->regmap, 0xe83f, &value); + if (value & 0x40) + break; + usleep_range(1000, 1100); + } + if (!retry && !(value & 0x40)) { + dev_err(dev, "%s: RAE is not ready to load\n", __func__); + return -ETIMEDOUT; + } + break; + case RT1321_DEV_ID: + /* RAE CRC clear */ + regmap_write(rt1320->regmap, 0x2000300e, 0xc0); + regmap_write(rt1320->regmap, 0x2000300f, 0x0f); + /* RAE stop & Phase sync & CRC disable */ + regmap_update_bits(rt1320->regmap, 0x20003003, 0xfe, 0x00); + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x00); + /* check whether write state is ready */ + while (--retry) { + regmap_read(rt1320->regmap, 0x20003043, &value); + if (value & 0x40) + break; + usleep_range(1000, 1100); + } + if (!retry && !(value & 0x40)) { + dev_err(dev, "%s: RAE is not ready to load\n", __func__); + return -ETIMEDOUT; + } + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; } dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size); @@ -1551,7 +2053,6 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) } regcache_cache_bypass(rt1320->regmap, false); - release_firmware(rae_fw); } else { dev_err(dev, "%s: Failed to load %s firmware\n", __func__, rae_filename); @@ -1559,18 +2060,34 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) goto _exit_; } - /* RAE CRC enable */ - regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); - - /* RAE update */ - regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); - regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); - - /* RAE run */ - regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); - - regmap_read(rt1320->regmap, 0xe80b, &value); - dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + /* RAE CRC enable */ + regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); + /* RAE update */ + regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); + /* RAE run */ + regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); + regmap_read(rt1320->regmap, 0xe80b, &value); + dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); + break; + case RT1321_DEV_ID: + /* RAE CRC enable */ + regmap_update_bits(rt1320->regmap, 0x20003003, 0x30, 0x30); + /* RAE update */ + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x80); + regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x80); + regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x80); + /* RAE run */ + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x01); + /* Phase sync eanble */ + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); + break; + } rt1320->rae_update_done = true; @@ -1598,32 +2115,21 @@ struct rt1320_dspfwheader { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); struct device *dev = &rt1320->sdw_slave->dev; - unsigned int val, i, fw_offset, fw_ready; - unsigned int fw_status_addr; + unsigned int val, i, fw_offset; struct rt1320_dspfwheader *fwheader; struct rt1320_imageinfo *ptr_img; struct sdw_bpt_section sec[10]; - const struct firmware *fw = NULL; + const struct firmware *fw __free(firmware) = NULL; unsigned char *fw_data; bool dev_fw_match = false; static const char hdr_sig[] = "AFX"; unsigned int hdr_size = 0; const char *dmi_vendor, *dmi_product, *dmi_sku; int len_vendor, len_product, len_sku; + unsigned char boot_mode = 0; /* 0: from RAM; 1: from ROM */ + unsigned char has_0x3fc00000 = 0; char filename[512]; - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return; - } - dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME); dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); @@ -1653,17 +2159,12 @@ struct rt1320_dspfwheader { RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (fw_ready) { + if (rt1320_dspfw_status(rt1320)) { dev_dbg(dev, "%s, DSP FW was already\n", __func__); rt1320->fw_load_done = true; goto _exit_; } - /* change to IRAM */ - regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); - request_firmware(&fw, filename, dev); if (fw) { fwheader = (struct rt1320_dspfwheader *)fw->data; @@ -1672,7 +2173,6 @@ struct rt1320_dspfwheader { if (fwheader->sync != 0x0a1c5679) { dev_err(dev, "%s: FW sync error\n", __func__); - release_firmware(fw); goto _exit_; } @@ -1709,9 +2209,11 @@ struct rt1320_dspfwheader { dev_fw_match = true; break; case RT1321_DEV_ID: - if (ptr_img->addr == 0x3fc00000) - if (fw_data[9] == '1') + if (ptr_img->addr == 0x3fc00000) { + if (fw_data[7] == '1') dev_fw_match = true; + has_0x3fc00000 = 1; + } break; default: dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); @@ -1721,6 +2223,22 @@ struct rt1320_dspfwheader { fw_offset += ptr_img->size; } + if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) { + /* + * For 1321 VA2, if the FW doesn't include the section for address 0x3fc00000, + * it means the FW will boot from ROM and force dev_fw_match to true to download FW by BRA. + */ + if (!has_0x3fc00000) { + boot_mode = 1; + dev_fw_match = true; + } + dev_dbg(dev, "%s: Boot from %s for VA2\n", __func__, (boot_mode ? "ROM" : "RAM")); + } + + /* change to IRAM */ + if (!boot_mode) + regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); + if (dev_fw_match) { dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__); rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num; @@ -1732,7 +2250,6 @@ struct rt1320_dspfwheader { } regcache_cache_bypass(rt1320->regmap, false); - release_firmware(fw); if (!dev_fw_match) { dev_err(dev, "%s: FW file doesn't match to device\n", __func__); @@ -1743,24 +2260,39 @@ struct rt1320_dspfwheader { goto _exit_; } - /* run RAM code */ - regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); - val |= 0x8; - regmap_write(rt1320->regmap, 0x3fc2bfc0, val); - - /* clear frame counter */ switch (rt1320->dev_id) { case RT1320_DEV_ID: + /* run RAM code */ + regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); + val |= 0x8; + regmap_write(rt1320->regmap, 0x3fc2bfc0, val); + /* clear frame counter */ regmap_write(rt1320->regmap, 0x3fc2bfcb, 0x00); regmap_write(rt1320->regmap, 0x3fc2bfca, 0x00); regmap_write(rt1320->regmap, 0x3fc2bfc9, 0x00); regmap_write(rt1320->regmap, 0x3fc2bfc8, 0x00); break; case RT1321_DEV_ID: + if (!boot_mode) { + /* run RAM code */ + regmap_read(rt1320->regmap, 0x3fc2dfc0, &val); + val |= 0x8; + regmap_write(rt1320->regmap, 0x3fc2dfc0, val); + } + /* clear frame counter */ regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00); regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00); regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00); regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00); + /* enable handshake */ + regmap_write(rt1320->regmap, 0x3fc2dfc4, 0x00); + regmap_write(rt1320->regmap, 0xd470, 0xad); + /* minimum phase settings */ + regmap_write(rt1320->regmap, 0xc5c4, 0x10); + regmap_write(rt1320->regmap, 0x20003003, 0x31); + regmap_update_bits(rt1320->regmap, 0x20003002, 0x40, 0x00); + regmap_write(rt1320->regmap, 0xc5b3, 0x01); + regmap_write(rt1320->regmap, 0xc052, 0x11); break; } @@ -1843,14 +2375,35 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320) static void rt1321_preset(struct rt1320_sdw_priv *rt1320) { + const struct reg_sequence *blindwrite; unsigned int i, reg, val, delay; + unsigned int array_size; + + switch (rt1320->version_id) { + case RT1321_VA0: + blindwrite = rt1321_blind_write; + array_size = ARRAY_SIZE(rt1321_blind_write); + break; + case RT1321_VA1: + blindwrite = rt1321_va1_blind_write; + array_size = ARRAY_SIZE(rt1321_va1_blind_write); + break; + case RT1321_VA2: + blindwrite = rt1321_va2_blind_write; + array_size = ARRAY_SIZE(rt1321_va2_blind_write); + break; + default: + dev_err(&rt1320->sdw_slave->dev, "%s: Unknown version ID %d\n", + __func__, rt1320->version_id); + return; + } - for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) { - reg = rt1321_blind_write[i].reg; - val = rt1321_blind_write[i].def; - delay = rt1321_blind_write[i].delay_us; + for (i = 0; i < array_size; i++) { + reg = blindwrite[i].reg; + val = blindwrite[i].def; + delay = blindwrite[i].delay_us; - if (reg == 0x3fc2dfc3) + if (reg == 0x1000cd56) rt1320_load_mcu_patch(rt1320); regmap_write(rt1320->regmap, reg, val); @@ -1892,6 +2445,24 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val); regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp); rt1320->dev_id = (val << 8) | tmp; + + /* This is a workaround that reads the value twice to obtain the correct result. */ + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); + val = (val << 8) | tmp; + + if (rt1320->dev_id == RT1321_DEV_ID) { + if (rt1320->version_id == 0x01) + rt1320->version_id = RT1321_VA2; + else if (val == RT1321_DEV_HV_VA0_ID) + rt1320->version_id = RT1321_VA0; + else if (val == RT1321_DEV_HV_VA1_ID) + rt1320->version_id = RT1321_VA1; + else + dev_err(dev, "%s: Unknown version ID 0x%x for RT1321\n", __func__, rt1320->version_id); + } } regmap_read(rt1320->regmap, @@ -2326,25 +2897,12 @@ static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) { struct device *dev = regmap_get_device(rt1320->regmap); - unsigned int fw_status_addr; - unsigned int fw_ready; + int dspfw_status; int ret = 0; if (!rt1320->r0_l_reg || !rt1320->r0_r_reg) return -EINVAL; - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return -EINVAL; - } - regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); ret = rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); @@ -2353,9 +2911,8 @@ static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) goto _timeout_; } - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (!fw_ready) { + dspfw_status = rt1320_dspfw_status(rt1320); + if (dspfw_status <= 0) { dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__); goto _timeout_; } @@ -2458,7 +3015,7 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, if (!rt1320->hw_init) return 0; - ret = pm_runtime_resume(component->dev); + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -2469,6 +3026,8 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, if (!ucontrol->value.integer.value[0]) rt1320->fw_load_done = false; + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); return 0; } @@ -2819,6 +3378,37 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, return 0; } +static int rt1320_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev); + unsigned int clk_base; + + regmap_read(rt1320->regmap, 0x004d, &clk_base); + dev_dbg(&rt1320->sdw_slave->dev, "%s clk_base=%x", __func__, clk_base); + + switch (clk_base) { + case RT1320_CLK_FREQ_19_2_MHZ: + case RT1320_CLK_FREQ_24MHZ: + regmap_write(rt1320->regmap, 0xc600, 0x04); + regmap_write(rt1320->regmap, 0xc601, 0x83); + regmap_write(rt1320->regmap, 0xc602, 0x1f); + regmap_write(rt1320->regmap, 0xc603, 0x40); + break; + case RT1320_CLK_FREQ_24_576MHZ: + case RT1320_CLK_FREQ_22_5792MHZ: + regmap_write(rt1320->regmap, 0xc600, 0x07); + regmap_write(rt1320->regmap, 0xc601, 0x80); + regmap_write(rt1320->regmap, 0xc602, 0x0f); + regmap_write(rt1320->regmap, 0xc603, 0x40); + break; + default: + return -EINVAL; + } + + return 0; +} + /* * slave_ops: callbacks for get_clock_stop_mode, clock_stop and * port_prep are not defined for now @@ -2826,12 +3416,13 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, static const struct sdw_slave_ops rt1320_slave_ops = { .read_prop = rt1320_read_prop, .update_status = rt1320_update_status, + .bus_config = rt1320_bus_config, }; static int rt1320_sdw_component_probe(struct snd_soc_component *component) { - int ret; struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + int ret; rt1320->component = component; @@ -3062,10 +3653,23 @@ static int rt1320_dev_resume(struct device *dev) return ret; regcache_cache_only(rt1320->regmap, false); - regcache_sync(rt1320->regmap); + ret = regcache_sync(rt1320->regmap); + if (ret) + goto err_sync; + regcache_cache_only(rt1320->mbq_regmap, false); - regcache_sync(rt1320->mbq_regmap); + ret = regcache_sync(rt1320->mbq_regmap); + if (ret) + goto err_sync; + return 0; + +err_sync: + regcache_cache_only(rt1320->regmap, true); + regcache_cache_only(rt1320->mbq_regmap, true); + regcache_mark_dirty(rt1320->regmap); + regcache_mark_dirty(rt1320->mbq_regmap); + return ret; } static const struct dev_pm_ops rt1320_pm = { diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index a7b573883dd0..df9ccdf92d36 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -17,12 +17,17 @@ #define RT1320_DEV_ID 0x6981 #define RT1321_DEV_ID 0x7045 +#define RT1321_DEV_HV_VA0_ID 0x6997 +#define RT1321_DEV_HV_VA1_ID 0x7071 /* imp-defined registers */ #define RT1320_DEV_VERSION_ID_1 0xc404 #define RT1320_DEV_ID_1 0xc405 #define RT1320_DEV_ID_0 0xc406 +#define RT1320_HV_DEV_ID_0 0xf622 +#define RT1320_HV_DEV_ID_1 0xf623 + #define RT1320_POWER_STATE 0xc560 #define RT1321_PATCH_MAIN_VER 0x1000cffe @@ -94,6 +99,12 @@ enum rt1320_version_id { RT1320_VC, }; +enum rt1321_version_id { + RT1321_VA0, + RT1321_VA1, + RT1321_VA2, +}; + #define RT1320_VER_B_ID 0x07392238 #define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin" #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin" @@ -121,6 +132,7 @@ struct rt1320_datafixpoint { int invrs; }; +/* FW parameter id 1300 */ typedef struct FwPara_HwSwGain { unsigned int SwAdvGain; unsigned int SwBasGain; @@ -163,6 +175,13 @@ enum rt1320_rw_type { RT1320_PARAM_READ = 3, }; +enum { + RT1320_CLK_FREQ_19_2_MHZ = 1, + RT1320_CLK_FREQ_24MHZ = 2, + RT1320_CLK_FREQ_24_576MHZ = 3, + RT1320_CLK_FREQ_22_5792MHZ = 4, +}; + struct rt1320_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 63b5fc439773..f8c370106504 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -184,8 +184,10 @@ static const struct reg_default rt274_reg[] = { { 0x0023a000, 0x00000057 }, { 0x00270500, 0x00000400 }, { 0x00370500, 0x00000400 }, + { 0x00830000, 0x00000097 }, { 0x00870500, 0x00000400 }, { 0x00920000, 0x00000031 }, + { 0x00930000, 0x00000097 }, { 0x00935000, 0x00000097 }, { 0x00936000, 0x00000097 }, { 0x00970500, 0x00000400 }, @@ -195,10 +197,12 @@ static const struct reg_default rt274_reg[] = { { 0x00c37000, 0x00000400 }, { 0x00c37100, 0x00000400 }, { 0x01270500, 0x00000400 }, + { 0x01270700, 0x00000000 }, { 0x01370500, 0x00000400 }, { 0x01371f00, 0x411111f0 }, { 0x01937000, 0x00000000 }, { 0x01970500, 0x00000400 }, + { 0x01970700, 0x00000020 }, { 0x02050000, 0x0000001b }, { 0x02139000, 0x00000080 }, { 0x0213a000, 0x00000080 }, @@ -207,10 +211,6 @@ static const struct reg_default rt274_reg[] = { { 0x02170700, 0x00000000 }, { 0x02270100, 0x00000000 }, { 0x02370100, 0x00000000 }, - { 0x01970700, 0x00000020 }, - { 0x00830000, 0x00000097 }, - { 0x00930000, 0x00000097 }, - { 0x01270700, 0x00000000 }, }; static bool rt274_volatile_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index ded0ea332480..4217467904d6 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -77,8 +77,10 @@ static const struct reg_default rt286_reg[] = { { 0x0023a000, 0x0000007f }, { 0x00270500, 0x00000400 }, { 0x00370500, 0x00000400 }, + { 0x00830000, 0x000000c3 }, { 0x00870500, 0x00000400 }, { 0x00920000, 0x00000031 }, + { 0x00930000, 0x000000c3 }, { 0x00935000, 0x000000c3 }, { 0x00936000, 0x000000c3 }, { 0x00970500, 0x00000400 }, @@ -88,16 +90,18 @@ static const struct reg_default rt286_reg[] = { { 0x00c37000, 0x00000000 }, { 0x00c37100, 0x00000080 }, { 0x01270500, 0x00000400 }, + { 0x01270700, 0x00000000 }, { 0x01370500, 0x00000400 }, { 0x01371f00, 0x411111f0 }, { 0x01439000, 0x00000080 }, { 0x0143a000, 0x00000080 }, - { 0x01470700, 0x00000000 }, + { 0x01470100, 0x00000000 }, { 0x01470500, 0x00000400 }, + { 0x01470700, 0x00000000 }, { 0x01470c00, 0x00000000 }, - { 0x01470100, 0x00000000 }, { 0x01837000, 0x00000000 }, { 0x01870500, 0x00000400 }, + { 0x01870700, 0x00000020 }, { 0x02050000, 0x00000000 }, { 0x02139000, 0x00000080 }, { 0x0213a000, 0x00000080 }, @@ -106,10 +110,6 @@ static const struct reg_default rt286_reg[] = { { 0x02170700, 0x00000000 }, { 0x02270100, 0x00000000 }, { 0x02370100, 0x00000000 }, - { 0x01870700, 0x00000020 }, - { 0x00830000, 0x000000c3 }, - { 0x00930000, 0x000000c3 }, - { 0x01270700, 0x00000000 }, }; static bool rt286_volatile_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 5414a1712b57..09aed08b5b79 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -78,8 +78,10 @@ static const struct reg_default rt298_reg[] = { { 0x0023a000, 0x0000007f }, { 0x00270500, 0x00000400 }, { 0x00370500, 0x00000400 }, + { 0x00830000, 0x000000c3 }, { 0x00870500, 0x00000400 }, { 0x00920000, 0x00000031 }, + { 0x00930000, 0x000000c3 }, { 0x00935000, 0x000000c3 }, { 0x00936000, 0x000000c3 }, { 0x00970500, 0x00000400 }, @@ -89,16 +91,18 @@ static const struct reg_default rt298_reg[] = { { 0x00c37000, 0x00000000 }, { 0x00c37100, 0x00000080 }, { 0x01270500, 0x00000400 }, + { 0x01270700, 0x00000000 }, { 0x01370500, 0x00000400 }, { 0x01371f00, 0x411111f0 }, { 0x01439000, 0x00000080 }, { 0x0143a000, 0x00000080 }, - { 0x01470700, 0x00000000 }, + { 0x01470100, 0x00000000 }, { 0x01470500, 0x00000400 }, + { 0x01470700, 0x00000000 }, { 0x01470c00, 0x00000000 }, - { 0x01470100, 0x00000000 }, { 0x01837000, 0x00000000 }, { 0x01870500, 0x00000400 }, + { 0x01870700, 0x00000020 }, { 0x02050000, 0x00000000 }, { 0x02139000, 0x00000080 }, { 0x0213a000, 0x00000080 }, @@ -107,10 +111,6 @@ static const struct reg_default rt298_reg[] = { { 0x02170700, 0x00000000 }, { 0x02270100, 0x00000000 }, { 0x02370100, 0x00000000 }, - { 0x01870700, 0x00000020 }, - { 0x00830000, 0x000000c3 }, - { 0x00930000, 0x000000c3 }, - { 0x01270700, 0x00000000 }, }; static bool rt298_volatile_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 199507d12841..91290bfe8daa 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -6,6 +6,7 @@ * Author: Oder Chiou <oder_chiou@realtek.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/input.h> #include <linux/spi/spi.h> @@ -79,17 +80,17 @@ static void rt5514_spi_copy_work(struct work_struct *work) unsigned int cur_wp, remain_data; u8 buf[8]; - mutex_lock(&rt5514_dsp->dma_lock); + guard(mutex)(&rt5514_dsp->dma_lock); if (!rt5514_dsp->substream) { dev_err(rt5514_dsp->dev, "No pcm substream\n"); - goto done; + return; } runtime = rt5514_dsp->substream->runtime; period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); if (!period_bytes) { schedule_delayed_work(&rt5514_dsp->copy_work, 5); - goto done; + return; } if (rt5514_dsp->buf_size % period_bytes) @@ -111,7 +112,7 @@ static void rt5514_spi_copy_work(struct work_struct *work) if (remain_data < period_bytes) { schedule_delayed_work(&rt5514_dsp->copy_work, 5); - goto done; + return; } } @@ -146,9 +147,6 @@ static void rt5514_spi_copy_work(struct work_struct *work) snd_pcm_period_elapsed(rt5514_dsp->substream); schedule_delayed_work(&rt5514_dsp->copy_work, 5); - -done: - mutex_unlock(&rt5514_dsp->dma_lock); } static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) @@ -216,7 +214,7 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, snd_soc_component_get_drvdata(component); u8 buf[8]; - mutex_lock(&rt5514_dsp->dma_lock); + guard(mutex)(&rt5514_dsp->dma_lock); rt5514_dsp->substream = substream; rt5514_dsp->dma_offset = 0; @@ -225,8 +223,6 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, if (buf[0] & RT5514_IRQ_STATUS_BIT) rt5514_schedule_copy(rt5514_dsp); - mutex_unlock(&rt5514_dsp->dma_lock); - return 0; } @@ -236,9 +232,8 @@ static int rt5514_spi_hw_free(struct snd_soc_component *component, struct rt5514_dsp *rt5514_dsp = snd_soc_component_get_drvdata(component); - mutex_lock(&rt5514_dsp->dma_lock); - rt5514_dsp->substream = NULL; - mutex_unlock(&rt5514_dsp->dma_lock); + scoped_guard(mutex, &rt5514_dsp->dma_lock) + rt5514_dsp->substream = NULL; cancel_delayed_work_sync(&rt5514_dsp->copy_work); diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 00a4a208d2fa..58aecd4c83a6 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1073,12 +1073,18 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, * settings to make sure recording properly. */ if (rt5514->dsp_enabled) { - rt5514->dsp_enabled = 0; - regmap_multi_reg_write(rt5514->i2c_regmap, - rt5514_i2c_patch, - ARRAY_SIZE(rt5514_i2c_patch)); + ret = regmap_multi_reg_write(rt5514->i2c_regmap, + rt5514_i2c_patch, + ARRAY_SIZE(rt5514_i2c_patch)); + if (ret) + return ret; + regcache_mark_dirty(rt5514->regmap); - regcache_sync(rt5514->regmap); + ret = regcache_sync(rt5514->regmap); + if (ret) + return ret; + + rt5514->dsp_enabled = 0; } } break; diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c index d5b3a57c8866..750f1f7169e6 100644 --- a/sound/soc/codecs/rt5575-spi.c +++ b/sound/soc/codecs/rt5575-spi.c @@ -93,7 +93,6 @@ static void rt5575_spi_burst_write(struct spi_device *spi, u32 addr, const u8 *t int rt5575_spi_fw_load(struct spi_device *spi) { struct device *dev = &spi->dev; - const struct firmware *firmware; int i, ret; static const char * const fw_path[] = { "realtek/rt5575/rt5575_fw1.bin", @@ -104,6 +103,7 @@ int rt5575_spi_fw_load(struct spi_device *spi) static const u32 fw_addr[] = { 0x5f400000, 0x5f600000, 0x5f7fe000, 0x5f7ff000 }; for (i = 0; i < ARRAY_SIZE(fw_addr); i++) { + const struct firmware *firmware __free(firmware) = NULL; ret = request_firmware(&firmware, fw_path[i], dev); if (ret) { dev_err(dev, "Request firmware failure: %d\n", ret); @@ -111,7 +111,6 @@ int rt5575_spi_fw_load(struct spi_device *spi) } rt5575_spi_burst_write(spi, fw_addr[i], firmware->data, firmware->size); - release_firmware(firmware); } return 0; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index e7e39671a576..7313ea71c958 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -6,6 +6,7 @@ * Author: Bard Liao <bardliao@realtek.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -3323,93 +3324,90 @@ static void rt5645_jack_detect_work(struct work_struct *work) if (!rt5645->component) return; - mutex_lock(&rt5645->jd_mutex); - - switch (rt5645->pdata.jd_mode) { - case 0: /* Not using rt5645 JD */ - if (rt5645->gpiod_hp_det) { - gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); - if (rt5645->pdata.inv_hp_pol) - gpio_state ^= 1; - dev_dbg(rt5645->component->dev, "gpio_state = %d\n", - gpio_state); - report = rt5645_jack_detect(rt5645->component, gpio_state); + scoped_guard(mutex, &rt5645->jd_mutex) { + switch (rt5645->pdata.jd_mode) { + case 0: /* Not using rt5645 JD */ + if (rt5645->gpiod_hp_det) { + gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); + if (rt5645->pdata.inv_hp_pol) + gpio_state ^= 1; + dev_dbg(rt5645->component->dev, "gpio_state = %d\n", + gpio_state); + report = rt5645_jack_detect(rt5645->component, gpio_state); + } + snd_soc_jack_report(rt5645->hp_jack, + report, SND_JACK_HEADPHONE); + snd_soc_jack_report(rt5645->mic_jack, + report, SND_JACK_MICROPHONE); + return; + case 4: + val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; + break; + default: /* read rt5645 jd1_1 status */ + val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; + break; } - snd_soc_jack_report(rt5645->hp_jack, - report, SND_JACK_HEADPHONE); - snd_soc_jack_report(rt5645->mic_jack, - report, SND_JACK_MICROPHONE); - mutex_unlock(&rt5645->jd_mutex); - return; - case 4: - val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; - break; - default: /* read rt5645 jd1_1 status */ - val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; - break; - } - - if (!val && (rt5645->jack_type == 0)) { /* jack in */ - report = rt5645_jack_detect(rt5645->component, 1); - } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { - /* for push button and jack out */ - btn_type = 0; - if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { - /* button pressed */ - report = SND_JACK_HEADSET; - btn_type = rt5645_button_detect(rt5645->component); - /* rt5650 can report three kinds of button behavior, - one click, double click and hold. However, - currently we will report button pressed/released - event. So all the three button behaviors are - treated as button pressed. */ - switch (btn_type) { - case 0x8000: - case 0x4000: - case 0x2000: - report |= SND_JACK_BTN_0; - break; - case 0x1000: - case 0x0800: - case 0x0400: - report |= SND_JACK_BTN_1; - break; - case 0x0200: - case 0x0100: - case 0x0080: - report |= SND_JACK_BTN_2; - break; - case 0x0040: - case 0x0020: - case 0x0010: - report |= SND_JACK_BTN_3; - break; - case 0x0000: /* unpressed */ - break; - default: - dev_err(rt5645->component->dev, - "Unexpected button code 0x%04x\n", - btn_type); - break; + if (!val && rt5645->jack_type == 0) { /* jack in */ + report = rt5645_jack_detect(rt5645->component, 1); + } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { + /* for push button and jack out */ + btn_type = 0; + if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { + /* button pressed */ + report = SND_JACK_HEADSET; + btn_type = rt5645_button_detect(rt5645->component); + /* + * rt5650 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + report |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + report |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + report |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + report |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + dev_err(rt5645->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } } + if (btn_type == 0)/* button release */ + report = rt5645->jack_type; + else + mod_timer(&rt5645->btn_check_timer, + msecs_to_jiffies(100)); + } else { + /* jack out */ + report = 0; + snd_soc_component_update_bits(rt5645->component, + RT5645_INT_IRQ_ST, 0x1, 0x0); + rt5645_jack_detect(rt5645->component, 0); } - if (btn_type == 0)/* button release */ - report = rt5645->jack_type; - else { - mod_timer(&rt5645->btn_check_timer, - msecs_to_jiffies(100)); - } - } else { - /* jack out */ - report = 0; - snd_soc_component_update_bits(rt5645->component, - RT5645_INT_IRQ_ST, 0x1, 0x0); - rt5645_jack_detect(rt5645->component, 0); } - mutex_unlock(&rt5645->jd_mutex); - snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); if (rt5645->en_button_func) @@ -3522,9 +3520,15 @@ static int rt5645_suspend(struct snd_soc_component *component) static int rt5645_resume(struct snd_soc_component *component) { struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + int ret; regcache_cache_only(rt5645->regmap, false); - regcache_sync(rt5645->regmap); + ret = regcache_sync(rt5645->regmap); + if (ret) { + regcache_cache_only(rt5645->regmap, true); + regcache_mark_dirty(rt5645->regmap); + return ret; + } return 0; } @@ -4335,9 +4339,15 @@ static int rt5645_sys_suspend(struct device *dev) static int rt5645_sys_resume(struct device *dev) { struct rt5645_priv *rt5645 = dev_get_drvdata(dev); + int ret; regcache_cache_only(rt5645->regmap, false); - regcache_sync(rt5645->regmap); + ret = regcache_sync(rt5645->regmap); + if (ret) { + regcache_cache_only(rt5645->regmap, true); + regcache_mark_dirty(rt5645->regmap); + return ret; + } if (rt5645->hp_jack) { rt5645->jack_type = 0; diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 48f57cd0920d..1d987472ba42 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -6,6 +6,7 @@ * Author: Bard Liao <bardliao@realtek.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -1208,7 +1209,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work) usleep_range(10000, 15000); } - mutex_lock(&rt5665->calibrate_mutex); + guard(mutex)(&rt5665->calibrate_mutex); val = snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010; if (!val) { @@ -1274,8 +1275,6 @@ static void rt5665_jack_detect_handler(struct work_struct *work) schedule_delayed_work(&rt5665->jd_check_work, 0); else cancel_delayed_work_sync(&rt5665->jd_check_work); - - mutex_unlock(&rt5665->calibrate_mutex); } static const char * const rt5665_clk_sync[] = { @@ -4564,7 +4563,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) { int value, count; - mutex_lock(&rt5665->calibrate_mutex); + guard(mutex)(&rt5665->calibrate_mutex); regcache_cache_bypass(rt5665->regmap, true); @@ -4601,7 +4600,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) pr_err("HP Calibration Failure\n"); regmap_write(rt5665->regmap, RT5665_RESET, 0); regcache_cache_bypass(rt5665->regmap, false); - goto out_unlock; + rt5665->calibration_done = true; + return; } count++; @@ -4620,7 +4620,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) pr_err("MONO Calibration Failure\n"); regmap_write(rt5665->regmap, RT5665_RESET, 0); regcache_cache_bypass(rt5665->regmap, false); - goto out_unlock; + rt5665->calibration_done = true; + return; } count++; @@ -4635,9 +4636,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120); -out_unlock: rt5665->calibration_done = true; - mutex_unlock(&rt5665->calibrate_mutex); } static void rt5665_calibrate_handler(struct work_struct *work) diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index fed6de40b8c8..4154bad27610 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -6,6 +6,7 @@ * Author: Bard Liao <bardliao@realtek.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -986,7 +987,7 @@ static void rt5668_jack_detect_handler(struct work_struct *work) return; } - mutex_lock(&rt5668->calibrate_mutex); + guard(mutex)(&rt5668->calibrate_mutex); val = snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL) & RT5668_JDH_RS_MASK; @@ -1053,8 +1054,6 @@ static void rt5668_jack_detect_handler(struct work_struct *work) schedule_delayed_work(&rt5668->jd_check_work, 0); else cancel_delayed_work_sync(&rt5668->jd_check_work); - - mutex_unlock(&rt5668->calibrate_mutex); } static const struct snd_kcontrol_new rt5668_snd_controls[] = { @@ -2356,7 +2355,7 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) { int value, count; - mutex_lock(&rt5668->calibrate_mutex); + guard(mutex)(&rt5668->calibrate_mutex); rt5668_reset(rt5668->regmap); regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf); @@ -2400,9 +2399,6 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) /* restore settings */ regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4); regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000); - - mutex_unlock(&rt5668->calibrate_mutex); - } static int rt5668_i2c_probe(struct i2c_client *i2c) diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index 1bcafd5f4468..ebc527115ea5 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -6,6 +6,7 @@ * Author: Oder Chiou <oder_chiou@realtek.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/input.h> #include <linux/spi/spi.h> @@ -133,9 +134,8 @@ static int rt5677_spi_hw_params( struct rt5677_dsp *rt5677_dsp = snd_soc_component_get_drvdata(component); - mutex_lock(&rt5677_dsp->dma_lock); + guard(mutex)(&rt5677_dsp->dma_lock); rt5677_dsp->substream = substream; - mutex_unlock(&rt5677_dsp->dma_lock); return 0; } @@ -147,9 +147,8 @@ static int rt5677_spi_hw_free( struct rt5677_dsp *rt5677_dsp = snd_soc_component_get_drvdata(component); - mutex_lock(&rt5677_dsp->dma_lock); + guard(mutex)(&rt5677_dsp->dma_lock); rt5677_dsp->substream = NULL; - mutex_unlock(&rt5677_dsp->dma_lock); return 0; } @@ -311,17 +310,17 @@ static void rt5677_spi_copy_work(struct work_struct *work) int ret = 0; /* Ensure runtime->dma_area buffer does not go away while copying. */ - mutex_lock(&rt5677_dsp->dma_lock); + guard(mutex)(&rt5677_dsp->dma_lock); if (!rt5677_dsp->substream) { dev_err(rt5677_dsp->dev, "No pcm substream\n"); - goto done; + return; } runtime = rt5677_dsp->substream->runtime; if (rt5677_spi_mic_write_offset(&mic_write_offset)) { dev_err(rt5677_dsp->dev, "No mic_write_offset\n"); - goto done; + return; } /* If this is the first time that we've asked for streaming data after @@ -355,7 +354,7 @@ static void rt5677_spi_copy_work(struct work_struct *work) ret = rt5677_spi_copy(rt5677_dsp, copy_bytes); if (ret) { dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret); - goto done; + return; } rt5677_dsp->avail_bytes += copy_bytes; if (rt5677_dsp->avail_bytes >= period_bytes) { @@ -367,8 +366,6 @@ static void rt5677_spi_copy_work(struct work_struct *work) delay = bytes_to_frames(runtime, period_bytes) / runtime->rate; schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay)); -done: - mutex_unlock(&rt5677_dsp->dma_lock); } static int rt5677_spi_pcm_new(struct snd_soc_component *component, @@ -507,10 +504,8 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) header[3] = ((addr + offset) & 0x0000ff00) >> 8; header[4] = ((addr + offset) & 0x000000ff) >> 0; - mutex_lock(&spi_mutex); - status |= spi_sync(g_spi, &m); - mutex_unlock(&spi_mutex); - + scoped_guard(mutex, &spi_mutex) + status |= spi_sync(g_spi, &m); /* Copy data back to caller buffer */ rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len); @@ -564,9 +559,8 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) offset += t.len; t.len += RT5677_SPI_HEADER + 1; - mutex_lock(&spi_mutex); - status |= spi_sync(g_spi, &m); - mutex_unlock(&spi_mutex); + scoped_guard(mutex, &spi_mutex) + status |= spi_sync(g_spi, &m); } return status; } @@ -591,10 +585,10 @@ void rt5677_spi_hotword_detected(void) return; } - mutex_lock(&rt5677_dsp->dma_lock); - dev_info(rt5677_dsp->dev, "Hotword detected\n"); - rt5677_dsp->new_hotword = true; - mutex_unlock(&rt5677_dsp->dma_lock); + scoped_guard(mutex, &rt5677_dsp->dma_lock) { + dev_info(rt5677_dsp->dev, "Hotword detected\n"); + rt5677_dsp->new_hotword = true; + } schedule_delayed_work(&rt5677_dsp->copy_work, 0); } diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index ac084ca008f3..4757017cc83d 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -6,6 +6,8 @@ * Author: Oder Chiou <oder_chiou@realtek.com> */ +#include <linux/cleanup.h> +#include <linux/bits.h> #include <linux/delay.h> #include <linux/firmware.h> #include <linux/fs.h> @@ -563,46 +565,43 @@ static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677, struct snd_soc_component *component = rt5677->component; int ret; - mutex_lock(&rt5677->dsp_cmd_lock); + guard(mutex)(&rt5677->dsp_cmd_lock); ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, addr >> 16); if (ret < 0) { dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, addr & 0xffff); if (ret < 0) { dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, value >> 16); if (ret < 0) { dev_err(component->dev, "Failed to set data msb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, value & 0xffff); if (ret < 0) { dev_err(component->dev, "Failed to set data lsb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, opcode); if (ret < 0) { dev_err(component->dev, "Failed to set op code value: %d\n", ret); - goto err; + return ret; } -err: - mutex_unlock(&rt5677->dsp_cmd_lock); - return ret; } @@ -622,36 +621,33 @@ static int rt5677_dsp_mode_i2c_read_addr( int ret; unsigned int msb, lsb; - mutex_lock(&rt5677->dsp_cmd_lock); + guard(mutex)(&rt5677->dsp_cmd_lock); ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, addr >> 16); if (ret < 0) { dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, addr & 0xffff); if (ret < 0) { dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, 0x0002); if (ret < 0) { dev_err(component->dev, "Failed to set op code value: %d\n", ret); - goto err; + return ret; } regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb); regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb); *value = (msb << 16) | lsb; -err: - mutex_unlock(&rt5677->dsp_cmd_lock); - return ret; } @@ -853,11 +849,11 @@ static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf, static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) { - const struct firmware *fwp; struct device *dev = rt5677->component->dev; - int ret = 0; + int ret; /* Load dsp firmware from rt5677_elf_vad file */ + const struct firmware *fwp __free(firmware) = NULL; ret = request_firmware(&fwp, "rt5677_elf_vad", dev); if (ret) { dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret); @@ -865,9 +861,7 @@ static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) } dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size); - ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); - release_firmware(fwp); - return ret; + return rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); } static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on) @@ -940,21 +934,20 @@ static void rt5677_dsp_work(struct work_struct *work) activity = false; /* Don't turn off the DSP while handling irqs */ - mutex_lock(&rt5677->irq_lock); - /* Set DSP CPU to Stop */ - regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, - RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); + scoped_guard(mutex, &rt5677->irq_lock) { + /* Set DSP CPU to Stop */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); - rt5677_set_dsp_mode(rt5677, false); + rt5677_set_dsp_mode(rt5677, false); - /* Disable and clear VAD interrupt */ - regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); - - /* Set GPIO1 pin back to be IRQ output for jack detect */ - regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, - RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); + /* Disable and clear VAD interrupt */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); - mutex_unlock(&rt5677->irq_lock); + /* Set GPIO1 pin back to be IRQ output for jack detect */ + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, + RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); + } } } @@ -4767,6 +4760,21 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) return rt5677_update_gpio_bits(rt5677, offset, m, v); } +static int rt5677_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + unsigned int shift = RT5677_GPIOx_DIR_SFT + (offset % 5) * 3; + unsigned int bank = offset / 5; + unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2; + int ret; + + ret = regmap_test_bits(rt5677->regmap, reg, BIT(shift)); + if (ret < 0) + return ret; + + return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + /* * Configures the GPIO as * 0 - floating @@ -4834,6 +4842,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) static const struct gpio_chip rt5677_template_chip = { .label = RT5677_DRV_NAME, .owner = THIS_MODULE, + .get_direction = rt5677_gpio_get_direction, .direction_output = rt5677_gpio_direction_out, .set = rt5677_gpio_set, .direction_input = rt5677_gpio_direction_in, @@ -4980,11 +4989,11 @@ static int rt5677_read(void *context, unsigned int reg, unsigned int *val) if (rt5677->is_dsp_mode) { if (reg > 0xff) { - mutex_lock(&rt5677->dsp_pri_lock); - rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, - reg & 0xff); - rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); - mutex_unlock(&rt5677->dsp_pri_lock); + scoped_guard(mutex, &rt5677->dsp_pri_lock) { + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, + reg & 0xff); + rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); + } } else { rt5677_dsp_mode_i2c_read(rt5677, reg, val); } @@ -5002,12 +5011,12 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val) if (rt5677->is_dsp_mode) { if (reg > 0xff) { - mutex_lock(&rt5677->dsp_pri_lock); - rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, - reg & 0xff); - rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, - val); - mutex_unlock(&rt5677->dsp_pri_lock); + scoped_guard(mutex, &rt5677->dsp_pri_lock) { + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, + reg & 0xff); + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, + val); + } } else { rt5677_dsp_mode_i2c_write(rt5677, reg, val); } @@ -5399,7 +5408,7 @@ static void rt5677_resume_irq_check(struct work_struct *work) * Without this explicit check, unplug the headset right after suspend * starts, then after resume the headset is still shown as plugged in. */ - mutex_lock(&rt5677->irq_lock); + guard(mutex)(&rt5677->irq_lock); for (i = 0; i < RT5677_IRQ_NUM; i++) { if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) { virq = irq_find_mapping(rt5677->domain, i); @@ -5407,7 +5416,6 @@ static void rt5677_resume_irq_check(struct work_struct *work) handle_nested_irq(virq); } } - mutex_unlock(&rt5677->irq_lock); } static void rt5677_irq_bus_lock(struct irq_data *data) diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index dec8c2147d68..23c7d2a9dbee 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -6,6 +6,7 @@ // Author: Oder Chiou <oder_chiou@realtek.com> // +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -410,7 +411,9 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) if (rt5682->first_hw_init) { regcache_cache_bypass(rt5682->regmap, false); regcache_mark_dirty(rt5682->regmap); - regcache_sync(rt5682->regmap); + ret = regcache_sync(rt5682->regmap); + if (ret) + goto err_sync; /* volatile registers */ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, @@ -472,8 +475,16 @@ reinit: /* Mark Slave initialization complete */ rt5682->hw_init = true; rt5682->first_hw_init = true; + goto out; + +err_sync: + regcache_cache_bypass(rt5682->regmap, false); + regcache_cache_only(rt5682->sdw_regmap, true); + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); err_nodev: +out: pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret); @@ -660,12 +671,11 @@ static int rt5682_interrupt_callback(struct sdw_slave *slave, dev_dbg(&slave->dev, "%s control_port_stat=%x", __func__, status->control_port); - mutex_lock(&rt5682->disable_irq_lock); + guard(mutex)(&rt5682->disable_irq_lock); if (status->control_port & 0x4 && !rt5682->disable_irq) { mod_delayed_work(system_power_efficient_wq, &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time)); } - mutex_unlock(&rt5682->disable_irq_lock); return 0; } @@ -736,11 +746,11 @@ static int rt5682_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt5682->disable_irq_lock); - rt5682->disable_irq = true; - ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, - SDW_SCP_INT1_IMPL_DEF, 0); - mutex_unlock(&rt5682->disable_irq_lock); + scoped_guard(mutex, &rt5682->disable_irq_lock) { + rt5682->disable_irq = true; + ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, + SDW_SCP_INT1_IMPL_DEF, 0); + } if (ret < 0) { /* log but don't prevent suspend from happening */ @@ -760,12 +770,12 @@ static int rt5682_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt5682->disable_irq_lock); - if (rt5682->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); - rt5682->disable_irq = false; + scoped_guard(mutex, &rt5682->disable_irq_lock) { + if (rt5682->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); + rt5682->disable_irq = false; + } } - mutex_unlock(&rt5682->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT); @@ -776,7 +786,13 @@ static int rt5682_dev_resume(struct device *dev) regcache_cache_only(rt5682->sdw_regmap, false); regcache_cache_only(rt5682->regmap, false); - regcache_sync(rt5682->regmap); + ret = regcache_sync(rt5682->regmap); + if (ret) { + regcache_cache_only(rt5682->sdw_regmap, true); + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 4b82e07d3b2c..6f7e68c20d13 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -6,6 +6,7 @@ // Author: Bard Liao <bardliao@realtek.com> // +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -3125,7 +3126,7 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) { int value, count; - mutex_lock(&rt5682->calibrate_mutex); + guard(mutex)(&rt5682->calibrate_mutex); rt5682_reset(rt5682); regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); @@ -3175,8 +3176,6 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c); - - mutex_unlock(&rt5682->calibrate_mutex); } EXPORT_SYMBOL_GPL(rt5682_calibrate); diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 3624067950c0..34997cfb465b 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -6,6 +6,7 @@ // Author: Derek Fang <derek.fang@realtek.com> // +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -648,7 +649,7 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode { struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); - mutex_lock(&rt5682s->sar_mutex); + guard(mutex)(&rt5682s->sar_mutex); switch (mode) { case SAR_PWR_SAVING: @@ -695,8 +696,6 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode); break; } - - mutex_unlock(&rt5682s->sar_mutex); } static void rt5682s_enable_push_button_irq(struct snd_soc_component *component) @@ -2534,7 +2533,7 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) if (!rt5682s_clk_check(rt5682s)) return -EINVAL; - mutex_lock(&rt5682s->wclk_mutex); + guard(mutex)(&rt5682s->wclk_mutex); snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, @@ -2556,8 +2555,6 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) rt5682s->wclk_enabled = 1; - mutex_unlock(&rt5682s->wclk_mutex); - return 0; } @@ -2570,7 +2567,7 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) if (!rt5682s_clk_check(rt5682s)) return; - mutex_lock(&rt5682s->wclk_mutex); + guard(mutex)(&rt5682s->wclk_mutex); if (!rt5682s->jack_type) snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, @@ -2585,8 +2582,6 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) rt5682s_set_pllb_power(rt5682s, 0); rt5682s->wclk_enabled = 0; - - mutex_unlock(&rt5682s->wclk_mutex); } static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw, @@ -2997,7 +2992,7 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) { unsigned int count, value; - mutex_lock(&rt5682s->calibrate_mutex); + guard(mutex)(&rt5682s->calibrate_mutex); regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80); usleep_range(15000, 20000); @@ -3034,8 +3029,6 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0); regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800); regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000); - - mutex_unlock(&rt5682s->calibrate_mutex); } static const struct regmap_config rt5682s_regmap = { diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index a451d5d1f8ab..3ba0cc37acd9 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/soundwire/sdw.h> @@ -415,12 +416,11 @@ static int rt700_interrupt_callback(struct sdw_slave *slave, dev_dbg(&slave->dev, "%s control_port_stat=%x", __func__, status->control_port); - mutex_lock(&rt700->disable_irq_lock); + guard(mutex)(&rt700->disable_irq_lock); if (status->control_port & 0x4 && !rt700->disable_irq) { mod_delayed_work(system_power_efficient_wq, &rt700->jack_detect_work, msecs_to_jiffies(250)); } - mutex_unlock(&rt700->disable_irq_lock); return 0; } @@ -458,10 +458,8 @@ static void rt700_sdw_remove(struct sdw_slave *slave) { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); - if (rt700->hw_init) { - cancel_delayed_work_sync(&rt700->jack_detect_work); - cancel_delayed_work_sync(&rt700->jack_btn_check_work); - } + cancel_delayed_work_sync(&rt700->jack_detect_work); + cancel_delayed_work_sync(&rt700->jack_btn_check_work); pm_runtime_disable(&slave->dev); } @@ -501,11 +499,11 @@ static int rt700_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt700->disable_irq_lock); - rt700->disable_irq = true; - ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, - SDW_SCP_INT1_IMPL_DEF, 0); - mutex_unlock(&rt700->disable_irq_lock); + scoped_guard(mutex, &rt700->disable_irq_lock) { + rt700->disable_irq = true; + ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, + SDW_SCP_INT1_IMPL_DEF, 0); + } if (ret < 0) { /* log but don't prevent suspend from happening */ diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h index 4ad0dcfd16fd..002f94493b01 100644 --- a/sound/soc/codecs/rt700-sdw.h +++ b/sound/soc/codecs/rt700-sdw.h @@ -313,17 +313,16 @@ static const struct reg_default rt700_reg_defaults[] = { { 0x3122, 0x0000 }, { 0x3123, 0x0000 }, { 0x7303, 0x0057 }, - { 0x7303, 0x0057 }, - { 0x8383, 0x0057 }, { 0x7308, 0x0097 }, - { 0x8388, 0x0097 }, { 0x7309, 0x0097 }, - { 0x8389, 0x0097 }, { 0x7312, 0x0000 }, - { 0x8392, 0x0000 }, { 0x7313, 0x0000 }, - { 0x8393, 0x0000 }, { 0x7319, 0x0000 }, + { 0x8383, 0x0057 }, + { 0x8388, 0x0097 }, + { 0x8389, 0x0097 }, + { 0x8392, 0x0000 }, + { 0x8393, 0x0000 }, { 0x8399, 0x0000 }, { 0x75201a, 0x8003 }, { 0x752045, 0x5289 }, diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index e028a1c3a9ac..e292b28b029e 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/soundwire/sdw_registers.h> @@ -415,13 +416,13 @@ static int rt711_sdca_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt711_sdca->disable_irq_lock); - rt711_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt711_sdca->disable_irq_lock); + scoped_guard(mutex, &rt711_sdca->disable_irq_lock) { + rt711_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + } if (ret1 < 0 || ret2 < 0) { /* log but don't prevent suspend from happening */ @@ -443,13 +444,15 @@ static int rt711_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt711->disable_irq_lock); - if (rt711->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt711->disable_irq = false; + scoped_guard(mutex, &rt711->disable_irq_lock) { + if (rt711->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + rt711->disable_irq = false; + } } - mutex_unlock(&rt711->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); diff --git a/sound/soc/codecs/rt711-sdca-sdw.h b/sound/soc/codecs/rt711-sdca-sdw.h index 0d774e473ab9..9aa22b52d556 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.h +++ b/sound/soc/codecs/rt711-sdca-sdw.h @@ -58,12 +58,12 @@ static const struct reg_default rt711_sdca_reg_defaults[] = { { 0x2f0f, 0x00 }, { 0x2f50, 0x03 }, { 0x2f5a, 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28, RT711_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, }; @@ -86,14 +86,14 @@ static const struct reg_default rt711_sdca_mbq_defaults[] = { { 0x610003f, 0xff12 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, }; #endif /* __RT711_SDW_SDCA_H__ */ diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 3a26c782d800..9781f289f8ad 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -450,7 +451,7 @@ io_error: static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) { - mutex_lock(&rt711->calibrate_mutex); + guard(mutex)(&rt711->calibrate_mutex); if (rt711->hs_jack) { /* Enable HID1 event & set button RTC mode */ @@ -515,8 +516,6 @@ static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); } - - mutex_unlock(&rt711->calibrate_mutex); } static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index a0c6a9efa840..4ad2b1da1954 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/soundwire/sdw.h> @@ -422,12 +423,11 @@ static int rt711_interrupt_callback(struct sdw_slave *slave, dev_dbg(&slave->dev, "%s control_port_stat=%x", __func__, status->control_port); - mutex_lock(&rt711->disable_irq_lock); + guard(mutex)(&rt711->disable_irq_lock); if (status->control_port & 0x4 && !rt711->disable_irq) { mod_delayed_work(system_power_efficient_wq, &rt711->jack_detect_work, msecs_to_jiffies(250)); } - mutex_unlock(&rt711->disable_irq_lock); return 0; } @@ -509,11 +509,11 @@ static int rt711_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt711->disable_irq_lock); - rt711->disable_irq = true; - ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, - SDW_SCP_INT1_IMPL_DEF, 0); - mutex_unlock(&rt711->disable_irq_lock); + scoped_guard(mutex, &rt711->disable_irq_lock) { + rt711->disable_irq = true; + ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, + SDW_SCP_INT1_IMPL_DEF, 0); + } if (ret < 0) { /* log but don't prevent suspend from happening */ @@ -535,12 +535,12 @@ static int rt711_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt711->disable_irq_lock); - if (rt711->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); - rt711->disable_irq = false; + scoped_guard(mutex, &rt711->disable_irq_lock) { + if (rt711->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); + rt711->disable_irq = false; + } } - mutex_unlock(&rt711->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h index 6acf9858330d..82da0e62d9d7 100644 --- a/sound/soc/codecs/rt711-sdw.h +++ b/sound/soc/codecs/rt711-sdw.h @@ -256,16 +256,16 @@ static const struct reg_default rt711_reg_defaults[] = { { 0x3122, 0x00 }, { 0x3123, 0x00 }, { 0x7303, 0x57 }, - { 0x8383, 0x57 }, { 0x7308, 0x97 }, - { 0x8388, 0x97 }, { 0x7309, 0x97 }, - { 0x8389, 0x97 }, { 0x7312, 0x00 }, - { 0x8392, 0x00 }, { 0x7313, 0x00 }, - { 0x8393, 0x00 }, { 0x7319, 0x00 }, + { 0x8383, 0x57 }, + { 0x8388, 0x97 }, + { 0x8389, 0x97 }, + { 0x8392, 0x00 }, + { 0x8393, 0x00 }, { 0x8399, 0x00 }, { 0x752008, 0xa807 }, { 0x752009, 0x1029 }, diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 5dbe9b67703e..3e49c3d0f25b 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -89,24 +90,24 @@ static int rt711_calibration(struct rt711_priv *rt711) struct regmap *regmap = rt711->regmap; int ret = 0; - mutex_lock(&rt711->calibrate_mutex); + guard(mutex)(&rt711->calibrate_mutex); regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); dev = regmap_get_device(regmap); /* Calibration manual mode */ rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, - 0xf, 0x0); + 0xf, 0x0); /* trigger */ rt711_index_update_bits(regmap, RT711_VENDOR_CALI, - RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, - RT711_DAC_DC_CALI_TRIGGER); + RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, + RT711_DAC_DC_CALI_TRIGGER); /* wait for calibration process */ rt711_index_read(regmap, RT711_VENDOR_CALI, - RT711_DAC_DC_CALI_CTL1, &val); + RT711_DAC_DC_CALI_CTL1, &val); while (val & RT711_DAC_DC_CALI_TRIGGER) { if (loop >= 500) { @@ -119,16 +120,15 @@ static int rt711_calibration(struct rt711_priv *rt711) usleep_range(10000, 11000); rt711_index_read(regmap, RT711_VENDOR_CALI, - RT711_DAC_DC_CALI_CTL1, &val); + RT711_DAC_DC_CALI_CTL1, &val); } /* depop mode */ rt711_index_update_bits(regmap, RT711_VENDOR_REG, - RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); + RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - mutex_unlock(&rt711->calibrate_mutex); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); return ret; @@ -362,24 +362,24 @@ static void rt711_jack_init(struct rt711_priv *rt711) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt711->component); - mutex_lock(&rt711->calibrate_mutex); + guard(mutex)(&rt711->calibrate_mutex); /* power on */ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); if (rt711->hs_jack) { /* unsolicited response & IRQ control */ regmap_write(rt711->regmap, - RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); regmap_write(rt711->regmap, - RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); + RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); regmap_write(rt711->regmap, - RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); rt711_index_write(rt711->regmap, RT711_VENDOR_REG, - 0x10, 0x2420); + 0x10, 0x2420); rt711_index_write(rt711->regmap, RT711_VENDOR_REG, - 0x19, 0x2e11); + 0x19, 0x2e11); switch (rt711->jd_src) { case RT711_JD1: @@ -449,8 +449,7 @@ static void rt711_jack_init(struct rt711_priv *rt711) /* power off */ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - mutex_unlock(&rt711->calibrate_mutex); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); } static int rt711_set_jack_detect(struct snd_soc_component *component, @@ -511,7 +510,7 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, unsigned int read_ll, read_rl; int i; - mutex_lock(&rt711->calibrate_mutex); + guard(mutex)(&rt711->calibrate_mutex); /* Can't use update bit function, so read the original value first */ addr_h = mc->reg; @@ -599,7 +598,6 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - mutex_unlock(&rt711->calibrate_mutex); return 0; } @@ -908,11 +906,11 @@ static int rt711_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_STANDBY: - mutex_lock(&rt711->calibrate_mutex); - regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, - AC_PWRST_D3); - mutex_unlock(&rt711->calibrate_mutex); + scoped_guard(mutex, &rt711->calibrate_mutex) { + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + } break; default: diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 85779547653e..8860d81134e7 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -916,10 +916,23 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev) } regcache_cache_only(rt712->regmap, false); - regcache_sync(rt712->regmap); + ret = regcache_sync(rt712->regmap); + if (ret) + goto err_sync; + regcache_cache_only(rt712->mbq_regmap, false); - regcache_sync(rt712->mbq_regmap); + ret = regcache_sync(rt712->mbq_regmap); + if (ret) + goto err_sync; + return 0; + +err_sync: + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + regcache_mark_dirty(rt712->regmap); + regcache_mark_dirty(rt712->mbq_regmap); + return ret; } static const struct dev_pm_ops rt712_sdca_dmic_pm = { diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h index 110154e74efe..2f08e58ed519 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.h +++ b/sound/soc/codecs/rt712-sdca-dmic.h @@ -36,6 +36,7 @@ struct rt712_sdca_dmic_kctrl_priv { #define CH_03 0x03 #define CH_04 0x04 +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_dmic_reg_defaults[] = { { 0x201a, 0x00 }, { 0x201b, 0x00 }, @@ -72,15 +73,16 @@ static const struct reg_default rt712_sdca_dmic_reg_defaults[] = { { 0x2f59, 0x07 }, { 0x3201, 0x01 }, { 0x320c, 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_dmic_mbq_defaults[] = { { 0x0590001e, 0x0020 }, { 0x06100000, 0x0010 }, diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 70d661ce2ef2..c50e74e20a88 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/module.h> @@ -427,13 +428,13 @@ static int rt712_sdca_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt712_sdca->disable_irq_lock); - rt712_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt712_sdca->disable_irq_lock); + scoped_guard(mutex, &rt712_sdca->disable_irq_lock) { + rt712_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + } if (ret1 < 0 || ret2 < 0) { /* log but don't prevent suspend from happening */ @@ -455,14 +456,15 @@ static int rt712_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt712->disable_irq_lock); - if (rt712->disable_irq == true) { - - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt712->disable_irq = false; + scoped_guard(mutex, &rt712->disable_irq_lock) { + if (rt712->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + rt712->disable_irq = false; + } } - mutex_unlock(&rt712->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); @@ -472,9 +474,20 @@ static int rt712_sdca_dev_resume(struct device *dev) } regcache_cache_only(rt712->regmap, false); - regcache_sync(rt712->regmap); + ret = regcache_sync(rt712->regmap); + if (ret) { + regcache_cache_only(rt712->regmap, true); + return ret; + } + regcache_cache_only(rt712->mbq_regmap, false); - regcache_sync(rt712->mbq_regmap); + ret = regcache_sync(rt712->mbq_regmap); + if (ret) { + regcache_cache_only(rt712->mbq_regmap, true); + regcache_cache_only(rt712->regmap, true); + return ret; + } + return 0; } diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h index 99fd2d67f04d..7ad25cc27f62 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.h +++ b/sound/soc/codecs/rt712-sdca-sdw.h @@ -11,21 +11,21 @@ #include <linux/regmap.h> #include <linux/soundwire/sdw_registers.h> +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_reg_defaults[] = { - - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, @@ -34,6 +34,7 @@ static const struct reg_default rt712_sdca_reg_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_mbq_defaults[] = { { 0x2000004, 0xaa01 }, { 0x200000e, 0x21e0 }, diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index d6353af07380..2218f9918ae3 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -7,6 +7,7 @@ // #include <linux/bitops.h> +#include <linux/cleanup.h> #include <sound/core.h> #include <linux/delay.h> #include <linux/init.h> @@ -403,7 +404,7 @@ io_error: static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) { - mutex_lock(&rt712->calibrate_mutex); + guard(mutex)(&rt712->calibrate_mutex); if (rt712->hs_jack) { /* Enable HID1 event & set button RTC mode */ @@ -450,8 +451,6 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__); } - - mutex_unlock(&rt712->calibrate_mutex); } static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h index 0cbc14844f8c..d408f2d7d0f2 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.h +++ b/sound/soc/codecs/rt715-sdca-sdw.h @@ -76,8 +76,6 @@ static const struct reg_default rt715_reg_defaults_sdca[] = { { 0x2f52, 0x01 }, { 0x2f5a, 0x02 }, { 0x2f5b, 0x05 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, - RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, @@ -98,14 +96,12 @@ static const struct reg_default rt715_reg_defaults_sdca[] = { RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, + RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, }; static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { @@ -149,8 +145,6 @@ static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, @@ -165,6 +159,8 @@ static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, }; diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h index 5d7661e335ae..f19fafb913f0 100644 --- a/sound/soc/codecs/rt715-sdw.h +++ b/sound/soc/codecs/rt715-sdw.h @@ -281,8 +281,8 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x371b, 0x00 }, { 0x371d, 0x00 }, { 0x3729, 0x00 }, - { 0x385e, 0x00 }, { 0x3859, 0x00 }, + { 0x385e, 0x00 }, { 0x4c12, 0x411111f0 }, { 0x4c13, 0x411111f0 }, { 0x4c1d, 0x411111f0 }, @@ -300,36 +300,36 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x4f1d, 0x411111f0 }, { 0x4f29, 0x411111f0 }, { 0x7207, 0x00 }, - { 0x8287, 0x00 }, { 0x7208, 0x00 }, - { 0x8288, 0x00 }, { 0x7209, 0x00 }, - { 0x8289, 0x00 }, { 0x7227, 0x00 }, - { 0x82a7, 0x00 }, { 0x7307, 0x97 }, - { 0x8387, 0x97 }, { 0x7308, 0x97 }, - { 0x8388, 0x97 }, { 0x7309, 0x97 }, - { 0x8389, 0x97 }, { 0x7312, 0x00 }, - { 0x8392, 0x00 }, { 0x7313, 0x00 }, - { 0x8393, 0x00 }, { 0x7318, 0x00 }, - { 0x8398, 0x00 }, { 0x7319, 0x00 }, - { 0x8399, 0x00 }, { 0x731a, 0x00 }, - { 0x839a, 0x00 }, { 0x731b, 0x00 }, - { 0x839b, 0x00 }, { 0x731d, 0x00 }, - { 0x839d, 0x00 }, { 0x7327, 0x97 }, - { 0x83a7, 0x97 }, { 0x7329, 0x00 }, + { 0x8287, 0x00 }, + { 0x8288, 0x00 }, + { 0x8289, 0x00 }, + { 0x82a7, 0x00 }, + { 0x8387, 0x97 }, + { 0x8388, 0x97 }, + { 0x8389, 0x97 }, + { 0x8392, 0x00 }, + { 0x8393, 0x00 }, + { 0x8398, 0x00 }, + { 0x8399, 0x00 }, + { 0x839a, 0x00 }, + { 0x839b, 0x00 }, + { 0x839d, 0x00 }, + { 0x83a7, 0x97 }, { 0x83a9, 0x00 }, { 0x752039, 0xa500 }, }; diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index 041b381e582b..eae7d662efae 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/module.h> @@ -466,13 +467,13 @@ static int rt721_sdca_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt721_sdca->disable_irq_lock); - rt721_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt721_sdca->disable_irq_lock); + scoped_guard(mutex, &rt721_sdca->disable_irq_lock) { + rt721_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + } if (ret1 < 0 || ret2 < 0) { /* log but don't prevent suspend from happening */ @@ -494,13 +495,15 @@ static int rt721_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt721->disable_irq_lock); - if (rt721->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt721->disable_irq = false; + scoped_guard(mutex, &rt721->disable_irq_lock) { + if (rt721->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + rt721->disable_irq = false; + } } - mutex_unlock(&rt721->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT); @@ -510,9 +513,20 @@ static int rt721_sdca_dev_resume(struct device *dev) } regcache_cache_only(rt721->regmap, false); - regcache_sync(rt721->regmap); + ret = regcache_sync(rt721->regmap); + if (ret) { + regcache_cache_only(rt721->regmap, true); + return ret; + } + regcache_cache_only(rt721->mbq_regmap, false); - regcache_sync(rt721->mbq_regmap); + ret = regcache_sync(rt721->mbq_regmap); + if (ret) { + regcache_cache_only(rt721->mbq_regmap, true); + regcache_cache_only(rt721->regmap, true); + return ret; + } + return 0; } diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h index 214b31b82583..13d6227f3392 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.h +++ b/sound/soc/codecs/rt721-sdca-sdw.h @@ -29,14 +29,6 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { { 0x2f5b, 0x07 }, { 0x2f5c, 0x27 }, { 0x2f5d, 0x07 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01, - RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11, - RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, @@ -45,6 +37,14 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, @@ -53,30 +53,30 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { RT721_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F, RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26, RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31, - RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23, - RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23, + RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; static const struct reg_default rt721_sdca_mbq_defaults[] = { diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c index 35960c225224..159c35d19dba 100644 --- a/sound/soc/codecs/rt721-sdca.c +++ b/sound/soc/codecs/rt721-sdca.c @@ -5,7 +5,7 @@ // Copyright(c) 2024 Realtek Semiconductor Corp. // // - +#include <linux/cleanup.h> #include <linux/bitops.h> #include <sound/core.h> #include <linux/delay.h> @@ -289,7 +289,7 @@ static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) { - mutex_lock(&rt721->calibrate_mutex); + guard(mutex)(&rt721->calibrate_mutex); if (rt721->hs_jack) { sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); @@ -309,7 +309,6 @@ static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, RT721_GE_REL_CTRL1, 0x4000, 0x4000); } - mutex_unlock(&rt721->calibrate_mutex); } static int rt721_sdca_set_jack_detect(struct snd_soc_component *component, diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index 7482a9a8d198..5bc9aed58d14 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/module.h> @@ -517,13 +518,13 @@ static int rt722_sdca_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt722_sdca->disable_irq_lock); - rt722_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt722_sdca->disable_irq_lock); + scoped_guard(mutex, &rt722_sdca->disable_irq_lock) { + rt722_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + } if (ret1 < 0 || ret2 < 0) { /* log but don't prevent suspend from happening */ @@ -545,13 +546,15 @@ static int rt722_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt722->disable_irq_lock); - if (rt722->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt722->disable_irq = false; + scoped_guard(mutex, &rt722->disable_irq_lock) { + if (rt722->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + rt722->disable_irq = false; + } } - mutex_unlock(&rt722->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT); @@ -561,7 +564,13 @@ static int rt722_sdca_dev_resume(struct device *dev) } regcache_cache_only(rt722->regmap, false); - regcache_sync(rt722->regmap); + ret = regcache_sync(rt722->regmap); + if (ret) { + regcache_cache_only(rt722->regmap, true); + regcache_mark_dirty(rt722->regmap); + return ret; + } + return 0; } diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 6b509eb65d77..4cbe9e909585 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -6,6 +6,7 @@ // // +#include <linux/cleanup.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/dmi.h> @@ -294,7 +295,7 @@ io_error: static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) { - mutex_lock(&rt722->calibrate_mutex); + guard(mutex)(&rt722->calibrate_mutex); if (rt722->hs_jack) { /* set SCP_SDCA_IntMask1[0]=1 */ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, @@ -317,7 +318,6 @@ static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2, 0x4000, 0x4000); } - mutex_unlock(&rt722->calibrate_mutex); } static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, @@ -352,8 +352,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) static const char func_tag[] = "FUNC"; static const char xu_tag[] = "XU"; const char *dmi_vendor, *dmi_product, *dmi_sku; - char *cae_filename; - const struct firmware *cae_fw = NULL; unsigned int cae_st_spk, cae_st_hp, cae_st_mic; unsigned int func, value; unsigned int combined_val; @@ -385,7 +383,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) space = strchr(dmi_sku, ' '); s_len = space ? space - dmi_sku : strlen(dmi_sku); - cae_filename = kasprintf(GFP_KERNEL, + char *cae_filename __free(kfree) = + kasprintf(GFP_KERNEL, "realtek/rt722/rt722_RAE_%.*s_%.*s_%.*s.dat", v_len, dmi_vendor, p_len, dmi_product, @@ -399,8 +398,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) regmap_write(rt722->regmap, RT722_MIC_CAE_PARAM39, 0x5f); usleep_range(50000, 60000); + const struct firmware *cae_fw __free(firmware) = NULL; request_firmware(&cae_fw, cae_filename, dev); - kfree(cae_filename); if (!cae_fw) { dev_err(dev, "%s: Failed to load CAE firmware\n", __func__); return -ENOENT; @@ -555,7 +554,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) regcache_cache_bypass(rt722->regmap, false); rt722->cae_update_done = 1; dev_dbg(dev, "%s: CAE FW update done.\n", __func__); - release_firmware(cae_fw); return 0; verify_abort: @@ -565,7 +563,6 @@ verify_abort: out_release: rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, RT722_MISC_CTRL1, 0x8000, 0x0000); - release_firmware(cae_fw); dev_err(dev, "%s: CAE FW update aborted (ret=%d).\n", __func__, ret); return ret; } @@ -1906,6 +1903,9 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) rt722_sdca_amp_preset(rt722); rt722_sdca_jack_preset(rt722); + if (rt722->hs_jack && (!rt722->first_hw_init)) + rt722_sdca_jack_init(rt722); + if (rt722->first_hw_init) { regcache_cache_bypass(rt722->regmap, false); regcache_mark_dirty(rt722->regmap); diff --git a/sound/soc/codecs/rt766-sdca-sdw.c b/sound/soc/codecs/rt766-sdca-sdw.c new file mode 100644 index 000000000000..5d60c3bcc6cd --- /dev/null +++ b/sound/soc/codecs/rt766-sdca-sdw.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt766-sdca-sdw.c -- rt766 SDCA ALSA SoC audio driver +// +// Copyright(c) 2026 Realtek Semiconductor Corp. +// +// + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/sdca.h> +#include <sound/sdca_function.h> +#include "rt766-sdca.h" +#include "rt766-sdca-sdw.h" + +#define RT766_PROBE_TIMEOUT 5000 + +static bool rt766_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: + case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: + case 0xc344 ... 0xc345: + case 0xc900: + case 0xc920: + case 0xd540 ... 0xd542: + case 0xf01e: + case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: + case 0x310100: + case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: + case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: + case RT766_MUTE_REG(UAJ, USER_FU41, 1): + case RT766_MUTE_REG(UAJ, USER_FU41, 2): + case RT766_VOLUME_REG(UAJ, USER_FU41, 1): + case RT766_VOLUME_REG(UAJ, USER_FU41, 2): + case RT766_MUTE_REG(UAJ, USER_FU36, 1): + case RT766_MUTE_REG(UAJ, USER_FU36, 2): + case RT766_VOLUME_REG(UAJ, USER_FU36, 1): + case RT766_VOLUME_REG(UAJ, USER_FU36, 2): + case RT766_PDE_REQ_REG(UAJ, PDE47): + case RT766_PDE_REQ_REG(UAJ, PDE34): + case RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX): + case RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX): + case RT766_FUNC_STATUS_REG(UAJ): /* 0x40480000 */ + case RT766_PDE_ACTUAL_REG(UAJ, PDE47): /* 0x40481400 */ + case RT766_PDE_ACTUAL_REG(UAJ, PDE34): /* 0x40481480 */ + case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): + case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): + case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE): + case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): /* 0x40600490 */ + case RT766_PDE_REQ_REG(MIC, PDE11): + case RT766_MUTE_REG(MIC, USER_FU113, 1): + case RT766_MUTE_REG(MIC, USER_FU113, 2): + case RT766_MUTE_REG(MIC, USER_FU113, 3): + case RT766_MUTE_REG(MIC, USER_FU113, 4): + case RT766_VOLUME_REG(MIC, USER_FU113, 1): + case RT766_VOLUME_REG(MIC, USER_FU113, 2): + case RT766_VOLUME_REG(MIC, USER_FU113, 3): + case RT766_VOLUME_REG(MIC, USER_FU113, 4): + case RT766_FUNC_STATUS_REG(MIC): /* 0x40880000 */ + case RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX): + case RT766_PDE_ACTUAL_REG(MIC, PDE11): /* 0x40881500 */ + case RT766_FUNC_STATUS_REG(HID): /* 0x40c80000 */ + /* 0x40c80080 - 0x40c80098 */ + case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): + case RT766_MUTE_REG(AMP, USER_FU21, 1): + case RT766_MUTE_REG(AMP, USER_FU21, 2): + case RT766_VOLUME_REG(AMP, USER_FU21, 1): + case RT766_VOLUME_REG(AMP, USER_FU21, 2): + case RT766_PDE_REQ_REG(AMP, PDE23): + case RT766_FUNC_STATUS_REG(AMP): /* 0x41080000 */ + case RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER): + case RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX): + case RT766_PDE_ACTUAL_REG(AMP, PDE23): /* 0x41081980 */ + case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt766_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: + case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: + case 0xc344 ... 0xc345: + case 0xc900: + case 0xc920: + case 0xd540 ... 0xd542: + case 0xf01e: + case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: + case 0x310100: + case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: + case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: + case RT766_FUNC_STATUS_REG(UAJ): + case RT766_PDE_ACTUAL_REG(UAJ, PDE47): + case RT766_PDE_ACTUAL_REG(UAJ, PDE34): + case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): + case RT766_FUNC_STATUS_REG(MIC): + case RT766_PDE_ACTUAL_REG(MIC, PDE11): + case RT766_FUNC_STATUS_REG(HID): + case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): + case RT766_FUNC_STATUS_REG(AMP): + case RT766_PDE_ACTUAL_REG(AMP, PDE23): + case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static int rt766_sdca_mbq_size(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT766_VOLUME_REG(UAJ, USER_FU41, 1): + case RT766_VOLUME_REG(UAJ, USER_FU41, 2): + case RT766_VOLUME_REG(UAJ, USER_FU36, 1): + case RT766_VOLUME_REG(UAJ, USER_FU36, 2): + case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): + case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): + case RT766_VOLUME_REG(MIC, USER_FU113, 1): + case RT766_VOLUME_REG(MIC, USER_FU113, 2): + case RT766_VOLUME_REG(MIC, USER_FU113, 3): + case RT766_VOLUME_REG(MIC, USER_FU113, 4): + case RT766_VOLUME_REG(AMP, USER_FU21, 1): + case RT766_VOLUME_REG(AMP, USER_FU21, 2): + return 2; + default: + return 1; + } +} + +static const struct regmap_sdw_mbq_cfg rt766_sdca_mbq_cfg = { + .mbq_size = rt766_sdca_mbq_size, +}; + +static const struct regmap_config rt766_sdca_regmap = { + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt766_sdca_readable_register, + .volatile_reg = rt766_sdca_volatile_register, + .reg_defaults = rt766_sdca_defaults, + .num_reg_defaults = ARRAY_SIZE(rt766_sdca_defaults), + .max_register = SDW_SDCA_MAX_REGISTER, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt766_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt766_sdca_priv *rt766 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt766->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt766->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt766_sdca_jack_init set. + */ + sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK3, + SDW_SCP_SDCA_INTMASK_SDCA_16); + sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK4, + SDW_SCP_SDCA_INTMASK_SDCA_24); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt766->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt766_sdca_io_init(&slave->dev, slave); +} + +static int rt766_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int ret; + + ret = sdw_slave_read_prop(slave); + if (ret < 0) + return ret; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + /* + * SDCA interrupts are routed through SoundWire domain IRQ. + */ + prop->use_domain_irq = true; + + return 0; +} + +static const struct sdw_slave_ops rt766_sdca_slave_ops = { + .read_prop = rt766_sdca_read_prop, + .update_status = rt766_sdca_update_status, +}; + +static int rt766_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, + &rt766_sdca_regmap, &rt766_sdca_mbq_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt766_sdca_init(&slave->dev, regmap, slave); +} + +static void rt766_sdca_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); +} + +static const struct sdw_device_id rt766_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x766, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x767, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt766_sdca_id); + +static int rt766_sdca_dev_suspend(struct device *dev) +{ + struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); + + if (!rt766->hw_init) + return 0; + + regcache_cache_only(rt766->regmap, true); + return 0; +} + +static int rt766_sdca_dev_system_suspend(struct device *dev) +{ + struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt766->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt766->disable_irq_lock); + rt766->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK3, + SDW_SCP_SDCA_INTMASK_SDCA_16, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK4, + SDW_SCP_SDCA_INTMASK_SDCA_24, 0); + mutex_unlock(&rt766->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt766_sdca_dev_suspend(dev); +} + +static int rt766_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); + int ret; + + if (!rt766->first_hw_init) + return 0; + + if (!slave->unattach_request) { + mutex_lock(&rt766->disable_irq_lock); + if (rt766->disable_irq == true) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK3, SDW_SCP_SDCA_INTMASK_SDCA_16); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK4, SDW_SCP_SDCA_INTMASK_SDCA_24); + rt766->disable_irq = false; + } + mutex_unlock(&rt766->disable_irq_lock); + goto regmap_sync; + } + + ret = sdw_slave_wait_for_init(slave, RT766_PROBE_TIMEOUT); + if (ret) { + sdw_show_ping_status(slave->bus, true); + return ret; + } + +regmap_sync: + regcache_cache_only(rt766->regmap, false); + ret = regcache_sync(rt766->regmap); + if (ret) { + regcache_cache_only(rt766->regmap, true); + regcache_mark_dirty(rt766->regmap); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops rt766_sdca_pm = { + SYSTEM_SLEEP_PM_OPS(rt766_sdca_dev_system_suspend, rt766_sdca_dev_resume) + RUNTIME_PM_OPS(rt766_sdca_dev_suspend, rt766_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt766_sdca_sdw_driver = { + .driver = { + .name = "rt766-sdca", + .pm = pm_ptr(&rt766_sdca_pm), + }, + .probe = rt766_sdca_sdw_probe, + .remove = rt766_sdca_sdw_remove, + .ops = &rt766_sdca_slave_ops, + .id_table = rt766_sdca_id, +}; +module_sdw_driver(rt766_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("SND_SOC_SDCA"); diff --git a/sound/soc/codecs/rt766-sdca-sdw.h b/sound/soc/codecs/rt766-sdca-sdw.h new file mode 100644 index 000000000000..3e923a1ea2f8 --- /dev/null +++ b/sound/soc/codecs/rt766-sdca-sdw.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt766-sdca-sdw.h -- RT766 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2026 Realtek Semiconductor Corp. + */ + +#ifndef __RT766_SDW_H__ +#define __RT766_SDW_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw_registers.h> + +static const struct reg_default rt766_sdca_defaults[] = { + /* 0x40400289 - 0x4040028a */ + { RT766_MUTE_REG(UAJ, USER_FU41, 1), 0x01 }, + { RT766_MUTE_REG(UAJ, USER_FU41, 2), 0x01 }, + /* 0x40400291 - 0x40400292 */ + { RT766_VOLUME_REG(UAJ, USER_FU41, 1), 0x0000 }, + { RT766_VOLUME_REG(UAJ, USER_FU41, 2), 0x0000 }, + /* 0x40400789 - 0x4040078a */ + { RT766_MUTE_REG(UAJ, USER_FU36, 1), 0x01 }, + { RT766_MUTE_REG(UAJ, USER_FU36, 2), 0x01 }, + /* 0x40400791 - 0x40400792 */ + { RT766_VOLUME_REG(UAJ, USER_FU36, 1), 0x0000 }, + { RT766_VOLUME_REG(UAJ, USER_FU36, 2), 0x0000 }, + { RT766_PDE_REQ_REG(UAJ, PDE47), 0x03 }, /* 0x40401408 */ + { RT766_PDE_REQ_REG(UAJ, PDE34), 0x03 }, /* 0x40401488 */ + { RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480080 */ + { RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480880 */ + /* 0x40600259 - 0x4060025a */ + { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), 0xfe00 }, + { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), 0xfe00 }, + { RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), 0x00 }, /* 0x40600488 */ + + { RT766_PDE_REQ_REG(MIC, PDE11), 0x03 }, /* 0x40801508 */ + /* 0x40801809 - 0x4080180c */ + { RT766_MUTE_REG(MIC, USER_FU113, 1), 0x01 }, + { RT766_MUTE_REG(MIC, USER_FU113, 2), 0x01 }, + { RT766_MUTE_REG(MIC, USER_FU113, 3), 0x01 }, + { RT766_MUTE_REG(MIC, USER_FU113, 4), 0x01 }, + /* 0x40801811 - 0x40801814 */ + { RT766_VOLUME_REG(MIC, USER_FU113, 1), 0x0000 }, + { RT766_VOLUME_REG(MIC, USER_FU113, 2), 0x0000 }, + { RT766_VOLUME_REG(MIC, USER_FU113, 3), 0x0000 }, + { RT766_VOLUME_REG(MIC, USER_FU113, 4), 0x0000 }, + { RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40880900 */ + + /* 0x41000189 - 0x4100018a */ + { RT766_MUTE_REG(AMP, USER_FU21, 1), 0x01 }, + { RT766_MUTE_REG(AMP, USER_FU21, 2), 0x01 }, + /* 0x41000191 - 0x41000192 */ + { RT766_VOLUME_REG(AMP, USER_FU21, 1), 0x0000 }, + { RT766_VOLUME_REG(AMP, USER_FU21, 2), 0x0000 }, + { RT766_PDE_REQ_REG(AMP, PDE23), 0x03 }, /* 0x41001988 */ + { RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0x00 }, /* 0x41080200 */ + { RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x41081080 */ + +}; + +#endif /* __RT766_SDW_H__ */ diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c new file mode 100644 index 000000000000..64d763b96a06 --- /dev/null +++ b/sound/soc/codecs/rt766-sdca.c @@ -0,0 +1,1378 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt766-sdca.c -- rt766 SDCA ALSA SoC audio driver +// +// Copyright(c) 2026 Realtek Semiconductor Corp. +// +// + +#include <linux/bitops.h> +#include <sound/core.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <sound/initval.h> +#include <sound/jack.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/sdw.h> +#include <sound/sdca.h> +#include <sound/sdca_asoc.h> +#include <sound/sdca_function.h> +#include <sound/sdca_hid.h> +#include <sound/sdca_regmap.h> +#include <sound/sdca_interrupts.h> +#include <linux/slab.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include "rt766-sdca.h" +#include "rt-sdw-common.h" + +static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) +{ + struct rt766_sdca_priv *rt766 = interrupt->priv; + unsigned char *buf = NULL; + unsigned int offset, owner, length; + unsigned int det_mode, idx, val; + int ret; + + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), + &det_mode); + if (ret < 0) + goto io_error; + + /* get current UMP message owner */ + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), + &owner); + if (ret < 0) + goto io_error; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + if (det_mode) { + /* read UMP message length */ + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH), + &length); + if (ret < 0) + goto _end_btn_det_; + + /* read UMP message offset */ + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET), + &offset); + if (ret < 0) + goto _end_btn_det_; + + buf = devm_kzalloc(&rt766->slave->dev, length, GFP_KERNEL); + if (!buf) { + dev_err(&rt766->slave->dev, "%s: alloc buf failed\n", __func__); + goto _end_btn_det_; + } + + for (idx = 0; idx < length; idx++) { + ret = regmap_read(rt766->regmap, + RT766_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (rt766->hid) + hid_input_report(rt766->hid, HID_INPUT_REPORT, + buf, length, 1); + } + +_end_btn_det_: + if (buf) + devm_kfree(&rt766->slave->dev, buf); + + /* Host is owner, so set back to device */ + if (owner == 0) { + regmap_write(rt766->regmap, + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), 0x01); + } + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static irqreturn_t rt766_sdca_irq_btn_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct rt766_sdca_priv *rt766 = interrupt->priv; + + if (!rt766->hs_jack) + return IRQ_HANDLED; + + if (!rt766->component->card || !rt766->component->card->instantiated) + return IRQ_HANDLED; + + mutex_lock(&rt766->disable_irq_lock); + if (!rt766->disable_irq) + rt766_sdca_btn_detect(interrupt); + mutex_unlock(&rt766->disable_irq_lock); + return IRQ_HANDLED; +} + +static int rt766_sdca_headset_detect(struct rt766_sdca_priv *rt766) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), + &det_mode); + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt766->jack_type = 0; + break; + case 0x03: + rt766->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt766->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt766->regmap, + RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), + det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt766->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static irqreturn_t rt766_sdca_irq_jd_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct rt766_sdca_priv *rt766 = interrupt->priv; + + if (!rt766->hs_jack) + return IRQ_HANDLED; + + if (!rt766->component->card || !rt766->component->card->instantiated) + return IRQ_HANDLED; + + mutex_lock(&rt766->disable_irq_lock); + if (!rt766->disable_irq) + rt766_sdca_headset_detect(rt766); + mutex_unlock(&rt766->disable_irq_lock); + + dev_dbg(&rt766->slave->dev, + "in %s, jack_type=%d\n", __func__, rt766->jack_type); + + snd_soc_jack_report(rt766->hs_jack, rt766->jack_type, SND_JACK_HEADSET); + return IRQ_HANDLED; +} + +static void rt766_sdca_destroy_hid_device(struct sdca_interrupt *interrupt) +{ + struct rt766_sdca_priv *rt766 = interrupt->priv; + + hid_destroy_device(rt766->hid); +} + +static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, + struct sdca_function_data *function, + struct snd_soc_component *component, + struct sdca_interrupt_info *info, + bool enabled) +{ + struct device *dev = &rt766->slave->dev; + struct sdca_interrupt *interrupt; + struct sdca_control *control; + struct sdca_entity *entity; + irq_handler_t handler; + int i, j, irq, ret; + + for (i = 0; i < function->num_entities; i++) { + entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + control = &entity->controls[j]; + irq = control->interrupt_position; + + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { + case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + handler = rt766_sdca_irq_jd_handler; + break; + case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + handler = rt766_sdca_irq_btn_handler; + break; + default: + continue; + } + + interrupt = &info->irqs[irq]; + + if (enabled) { + ret = sdca_irq_data_populate(dev, rt766->regmap, component, + function, entity, control, + interrupt); + if (ret) + return ret; + + if (handler == rt766_sdca_irq_btn_handler) { + ret = sdca_add_hid_device(interrupt); + if (ret) + return ret; + + interrupt->free_priv = rt766_sdca_destroy_hid_device; + rt766->hid = interrupt->priv; + } + + interrupt->priv = rt766; + ret = sdca_irq_request(dev, info, irq, interrupt->name, + handler, interrupt); + if (ret) { + dev_err(dev, "failed to request irq %s: %d\n", + interrupt->name, ret); + sdca_irq_cleanup_late(dev, function, info); + return ret; + } + dev_dbg(dev, "Requesting IRQ %d InterruptName=%s\n", irq, interrupt->name); + } else { + sdca_irq_cleanup_late(dev, function, info); + dev_dbg(dev, "Freeing IRQ %d\n", irq); + } + } + } + + return 0; +} + +static int rt766_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int ret; + + if (!rt766->uaj_func_data) { + dev_err(&rt766->slave->dev, "The SDCA UAJ function is not supported.\n"); + return -EINVAL; + } + + rt766->hs_jack = hs_jack; + + if (!rt766->first_hw_init) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + /* disable interrupts if hs_jack is not set */ + if (!rt766->hs_jack) { + if (rt766->uaj_func_data) + rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, + rt766->component, rt766->irq_info, false); + + if (rt766->hid_func_data) + rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, + rt766->component, rt766->irq_info, false); + } + + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +static int rt766_sdca_set_fu_ctl(struct rt766_sdca_priv *rt766, int func_num, int fu_num) +{ + unsigned int fu01_reg, fu02_reg; + unsigned int ch_01, ch_02; + unsigned int ch_mute; + unsigned int fu_reg; + int err, i; + + switch (fu_num) { + case RT766_SDCA_ENT_USER_FU41: + ch_01 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_l_mute) ? 0x01 : 0x00; + ch_02 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_r_mute) ? 0x01 : 0x00; + break; + case RT766_SDCA_ENT_USER_FU36: + ch_01 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_l_mute) ? 0x01 : 0x00; + ch_02 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_r_mute) ? 0x01 : 0x00; + break; + case RT766_SDCA_ENT_USER_FU21: + ch_01 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_l_mute) ? 0x01 : 0x00; + ch_02 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_r_mute) ? 0x01 : 0x00; + break; + case RT766_SDCA_ENT_USER_FU113: + for (i = 0; i < ARRAY_SIZE(rt766->fu113_mixer_mute); i++) { + ch_mute = (rt766->fu113_dapm_mute || rt766->fu113_mixer_mute[i]) ? 0x01 : 0x00; + fu_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1) + i; + err = regmap_write(rt766->regmap, fu_reg, ch_mute); + if (err < 0) + return err; + } + return 0; + } + + fu01_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1); + fu02_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 2); + err = regmap_write(rt766->regmap, fu01_reg, ch_01); + if (err < 0) + return err; + err = regmap_write(rt766->regmap, fu02_reg, ch_02); + if (err < 0) + return err; + + return 0; +} + +static int rt766_sdca_fu41_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt766->fu41_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt766->fu41_mixer_r_mute; + return 0; +} + +static int rt766_sdca_fu41_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int err; + + if (rt766->fu41_mixer_l_mute == !ucontrol->value.integer.value[0] && + rt766->fu41_mixer_r_mute == !ucontrol->value.integer.value[1]) + return 0; + + rt766->fu41_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt766->fu41_mixer_r_mute = !ucontrol->value.integer.value[1]; + + err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); + if (err < 0) + return err; + + return 1; +} + +static int rt766_sdca_fu36_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt766->fu36_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt766->fu36_mixer_r_mute; + return 0; +} + +static int rt766_sdca_fu36_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int err; + + if (rt766->fu36_mixer_l_mute == !ucontrol->value.integer.value[0] && + rt766->fu36_mixer_r_mute == !ucontrol->value.integer.value[1]) + return 0; + + rt766->fu36_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt766->fu36_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); + if (err < 0) + return err; + + return 1; +} + +static int rt766_sdca_fu21_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt766->fu21_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt766->fu21_mixer_r_mute; + return 0; +} + +static int rt766_sdca_fu21_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int err; + + if (rt766->fu21_mixer_l_mute == !ucontrol->value.integer.value[0] && + rt766->fu21_mixer_r_mute == !ucontrol->value.integer.value[1]) + return 0; + + rt766->fu21_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt766->fu21_mixer_r_mute = !ucontrol->value.integer.value[1]; + + err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); + if (err < 0) + return err; + + return 1; +} + +static int rt766_sdca_fu113_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 rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt766->fu113_dapm_mute = false; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); + break; + case SND_SOC_DAPM_PRE_PMD: + rt766->fu113_dapm_mute = true; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); + break; + } + return 0; +} + +static int rt766_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct rt_sdca_dmic_kctrl_priv *p = + (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; + const unsigned int interval_offset = 0xc0; + unsigned int regvalue, ctl, i; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt766->regmap, p->reg_base + i, ®value); + ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt766_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt_sdca_dmic_kctrl_priv *p = + (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + const unsigned int interval_offset = 0xc0; + unsigned int gain_val[4]; + unsigned int i, changed = 0; + unsigned int regvalue[4]; + int err; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt766->regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt766->regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt766->slave->dev, "0x%08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +static int rt766_sdca_dmic_fu113_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + unsigned int i; + + for (i = 0; i < 4; i++) + ucontrol->value.integer.value[i] = !rt766->fu113_mixer_mute[i]; + return 0; +} + +static int rt766_sdca_dmic_fu113_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int err, changed = 0, i; + + for (i = 0; i < 4; i++) { + if (rt766->fu113_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt766->fu113_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); + if (err < 0) + return err; + return changed; +} + +static int rt766_sdca_fu41_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 rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt766->fu41_dapm_mute = false; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); + break; + case SND_SOC_DAPM_PRE_PMD: + rt766->fu41_dapm_mute = true; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); + break; + } + return 0; +} + +static int rt766_sdca_pde_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event, int func_num, int pde_num, const char *pde_ent) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct sdca_function_data *func_data; + unsigned char ps0 = 0x0, ps3 = 0x3; + const struct sdca_entity *entity; + unsigned int pde_req_reg; + int from_ps, to_ps; + int ret; + + pde_req_reg = SDW_SDCA_CTL(func_num, pde_num, SDCA_CTL_PDE_REQUESTED_PS, 0); + + switch (func_num) { + case RT766_FUNC_NUM_UAJ: + func_data = rt766->uaj_func_data; + break; + case RT766_FUNC_NUM_AMP: + func_data = rt766->sa_func_data; + break; + case RT766_FUNC_NUM_MIC: + func_data = rt766->sm_func_data; + break; + default: + dev_err(component->dev, "%s: unsupported func_num %d\n", + __func__, func_num); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt766->regmap, pde_req_reg, ps0); + from_ps = ps3; + to_ps = ps0; + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt766->regmap, pde_req_reg, ps3); + from_ps = ps0; + to_ps = ps3; + break; + } + + entity = sdca_find_entity_by_label(func_data, pde_ent); + if (!entity) { + dev_err(component->dev, "%s: failed to find entity %s\n", + __func__, pde_ent); + return -EINVAL; + } + + ret = sdca_asoc_pde_poll_actual_ps(rt766->regmap, + func_num, + pde_num, + from_ps, to_ps, + entity->pde.max_delay, + entity->pde.num_max_delay); + if (ret) + dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", + __func__, from_ps, to_ps, ret); + + return ret; +} + +static int rt766_sdca_pde47_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return rt766_sdca_pde_event(w, kcontrol, event, + RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE47, "PDE 47"); +} + +static int rt766_sdca_fu36_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 rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt766->fu36_dapm_mute = false; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); + break; + case SND_SOC_DAPM_PRE_PMD: + rt766->fu36_dapm_mute = true; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); + break; + } + return 0; +} + +static int rt766_sdca_pde34_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return rt766_sdca_pde_event(w, kcontrol, event, + RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE34, "PDE 34"); +} + +static int rt766_sdca_fu21_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 rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt766->fu21_dapm_mute = false; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); + break; + case SND_SOC_DAPM_PRE_PMD: + rt766->fu21_dapm_mute = true; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); + break; + } + return 0; +} + +static int rt766_sdca_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return rt766_sdca_pde_event(w, kcontrol, event, + RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_PDE23, "PDE 23"); +} + +static int rt766_sdca_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return rt766_sdca_pde_event(w, kcontrol, event, + RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_PDE11, "PDE 11"); +} + +static int rt766_dmic_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt_sdca_dmic_kctrl_priv *p = + (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +static const char * const rt766_rx_data_ch_select[] = { + "L,R", + "R,L", + "L,L", + "R,R", + "L,L+R", + "R,L+R", + "L+R,L", + "L+R,R", + "L+R,L+R", +}; + +static SOC_ENUM_SINGLE_DECL(rt766_rx_data_ch_enum, + RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0, + rt766_rx_data_ch_select); + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -9525, 75, 0); +static const DECLARE_TLV_DB_SCALE(spk_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, -200, 200, 0); + +#define RT766_P75DB_STEP 0xC0 /* 0.75 dB in Q7.8 format */ +#define RT766_2DB_STEP 0x200 /* 2 dB in Q7.8 format */ +#define RT766_HP_VOL_MIN (-127) /* -95.25 dB / 0.75 dB step */ +#define RT766_HS_VOL_MIN (-23) /* -17.25 dB / 0.75 dB step */ +#define RT766_HS_BOOST_VOL_MIN (-1) /* -2 dB / 2 dB step */ +#define RT766_SPK_VOL_MIN (-87) /* -65.25 dB / 0.75 dB step */ +#define RT766_P_VOL_MAX 0 /* 0 dB / 0.75 dB step */ +#define RT766_HS_VOL_MAX 40 /* 30 dB / 0.75 dB step */ +#define RT766_HS_BOOST_VOL_MAX 20 /* 40 dB / 2 dB step */ + +static const struct snd_kcontrol_new rt766_sdca_controls[] = { + SOC_DOUBLE_EXT("FU41 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt766_sdca_fu41_playback_get, rt766_sdca_fu41_playback_put), + SDCA_DOUBLE_Q78_TLV("FU41 Playback Volume", + RT766_VOLUME_REG(UAJ, USER_FU41, 1), + RT766_VOLUME_REG(UAJ, USER_FU41, 2), + RT766_HP_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, hp_vol_tlv), + SOC_DOUBLE_EXT("FU36 Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt766_sdca_fu36_capture_get, rt766_sdca_fu36_capture_put), + SDCA_DOUBLE_Q78_TLV("FU36 Capture Volume", + RT766_VOLUME_REG(UAJ, USER_FU36, 1), + RT766_VOLUME_REG(UAJ, USER_FU36, 2), + RT766_HS_VOL_MIN, RT766_HS_VOL_MAX, RT766_P75DB_STEP, mic_vol_tlv), + SDCA_DOUBLE_Q78_TLV("FU33 Boost Volume", + RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), + RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), + RT766_HS_BOOST_VOL_MIN, RT766_HS_BOOST_VOL_MAX, RT766_2DB_STEP, boost_vol_tlv), + + SOC_DOUBLE_EXT("FU21 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt766_sdca_fu21_playback_get, rt766_sdca_fu21_playback_put), + SDCA_DOUBLE_Q78_TLV("FU21 Playback Volume", + RT766_VOLUME_REG(AMP, USER_FU21, 1), + RT766_VOLUME_REG(AMP, USER_FU21, 2), + RT766_SPK_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, spk_vol_tlv), + SOC_ENUM("RX Channel Select", rt766_rx_data_ch_enum), + + RT_SDCA_FU_CTRL("FU113 Capture Switch", + RT766_MUTE_REG(MIC, USER_FU113, 1), 1, 1, 4, rt766_dmic_fu_info, + rt766_sdca_dmic_fu113_capture_get, rt766_sdca_dmic_fu113_capture_put), + RT_SDCA_EXT_TLV("FU113 Capture Volume", + RT766_VOLUME_REG(MIC, USER_FU113, 1), + rt766_sdca_dmic_set_gain_get, rt766_sdca_dmic_set_gain_put, + 4, 0x3f, mic_vol_tlv, rt766_dmic_fu_info), +}; + +static const struct snd_soc_dapm_widget rt766_sdca_dapm_widgets[] = { + /* UAJ */ + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, + rt766_sdca_pde47_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0, + rt766_sdca_pde34_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("FU 41", NULL, SND_SOC_NOPM, 0, 0, + rt766_sdca_fu41_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, + rt766_sdca_fu36_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP12TX", "DP12 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* AMP */ + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, + rt766_sdca_fu21_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt766_sdca_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* DMIC */ + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt766_sdca_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, + rt766_sdca_fu113_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt766_sdca_audio_map[] = { + { "FU 41", NULL, "DP3RX" }, + { "DP12TX", NULL, "FU 36" }, + { "FU 36", NULL, "PDE 34" }, + { "FU 36", NULL, "MIC2" }, + { "HP", NULL, "PDE 47" }, + { "HP", NULL, "FU 41" }, + + { "FU 21", NULL, "DP1RX" }, + { "FU 21", NULL, "PDE 23" }, + { "SPOL", NULL, "FU 21" }, + { "SPOR", NULL, "FU 21" }, + + {"DP8TX", NULL, "FU 113"}, + {"FU 113", NULL, "PDE 11"}, + {"FU 113", NULL, "DMIC1"}, + {"FU 113", NULL, "DMIC2"}, +}; + +static int rt766_sdca_probe(struct snd_soc_component *component) +{ + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct device *dev = &rt766->slave->dev; + int ret; + + rt766->component = component; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + if (rt766->uaj_func_data) { + dev_dbg(dev, "%s : irq %d\n", __func__, rt766->slave->irq); + + rt766->irq_info = devm_sdca_irq_allocate(dev, rt766->regmap, rt766->slave->irq); + if (IS_ERR(rt766->irq_info)) + return PTR_ERR(rt766->irq_info); + + ret = rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, + component, rt766->irq_info, true); + if (ret < 0) { + dev_err(dev, "Failed to request UAJ SDCA IRQ: %d\n", ret); + return ret; + } + + if (rt766->hid_func_data) { + ret = rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, + component, rt766->irq_info, true); + if (ret < 0) { + dev_err(dev, "Failed to request HID SDCA IRQ: %d\n", ret); + return ret; + } + } + } + + return 0; +} + +static void rt766_sdca_remove(struct snd_soc_component *component) +{ + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + sdca_irq_cleanup_late(component->dev, rt766->uaj_func_data, rt766->irq_info); + sdca_irq_cleanup_late(component->dev, rt766->hid_func_data, rt766->irq_info); +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt766 = { + .probe = rt766_sdca_probe, + .remove = rt766_sdca_remove, + .controls = rt766_sdca_controls, + .num_controls = ARRAY_SIZE(rt766_sdca_controls), + .dapm_widgets = rt766_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt766_sdca_dapm_widgets), + .dapm_routes = rt766_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt766_sdca_audio_map), + .set_jack = rt766_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt766_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt766_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt766_sdca_pcm_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 rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + unsigned int sampling_rate; + int retval, port; + + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt766->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + if (dai->id == RT766_AIF1) + port = 3; + else if (dai->id == RT766_AIF2) + port = 1; + else + return -EINVAL; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT766_AIF1) + port = 12; + else if (dai->id == RT766_AIF3) + port = 8; + else + return -EINVAL; + } + + port_config.num = port; + retval = sdw_stream_add_slave(rt766->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "%s: Unsupported channels %d\n", + __func__, params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT766_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT766_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT766_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT766_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + switch (dai->id) { + case RT766_AIF1: + regmap_write(rt766->regmap, + RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), + sampling_rate); + regmap_write(rt766->regmap, + RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), + sampling_rate); + break; + case RT766_AIF2: + regmap_write(rt766->regmap, + RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), + sampling_rate); + break; + case RT766_AIF3: + regmap_write(rt766->regmap, + RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), + sampling_rate); + break; + default: + dev_err(component->dev, "%s: Wrong DAI id\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int rt766_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt766->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt766->slave, sdw_stream); + return 0; +} + +#define RT766_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) +#define RT766_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define RT766_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops rt766_sdca_ops = { + .hw_params = rt766_sdca_pcm_hw_params, + .hw_free = rt766_sdca_pcm_hw_free, + .set_stream = rt766_sdca_set_sdw_stream, + .shutdown = rt766_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt766_sdca_dai[] = { + { + .name = "rt766-sdca-aif1", + .id = RT766_AIF1, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT766_STEREO_RATES, + .formats = RT766_DAC_FORMATS, + }, + .capture = { + .stream_name = "DP12 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT766_STEREO_RATES, + .formats = RT766_ADC_FORMATS, + }, + .ops = &rt766_sdca_ops, + .symmetric_rate = 1, + }, + { + .name = "rt766-sdca-aif2", + .id = RT766_AIF2, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = RT766_STEREO_RATES, + .formats = RT766_DAC_FORMATS, + }, + .ops = &rt766_sdca_ops, + }, + { + .name = "rt766-sdca-aif3", + .id = RT766_AIF3, + .capture = { + .stream_name = "DP8 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT766_STEREO_RATES, + .formats = RT766_ADC_FORMATS, + }, + .ops = &rt766_sdca_ops, + } +}; + +static unsigned int rt766_find_dt_rates(struct device *dev, struct sdca_function_data *function, + const char *label) +{ + struct snd_soc_pcm_stream stream; + struct sdca_entity *entity; + int i, ret; + + for (i = 0; i < function->num_entities; i++) { + entity = &function->entities[i]; + + if (strcmp(entity->label, label)) + continue; + + /* Can't check earlier as only terminals have an iot member. */ + if (!entity->iot.is_dataport) + continue; + + ret = sdca_asoc_populate_rate_format(dev, function, entity, &stream); + if (ret < 0) { + dev_dbg(dev, "%s: failed to parse rates for entity %s\n", + __func__, entity->label); + return 0; + } + + dev_dbg(dev, "%s: %s supports rates 0x%08x\n", __func__, entity->label, stream.rates); + } + + return stream.rates; +} + +int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) +{ + struct sdca_function_data *func_data_ptr; + struct snd_soc_dai_driver *dai_drv; + struct rt766_sdca_priv *rt766; + unsigned int rates; + int ret; + int i; + + rt766 = devm_kzalloc(dev, sizeof(*rt766), GFP_KERNEL); + if (!rt766) + return -ENOMEM; + + dev_set_drvdata(dev, rt766); + rt766->slave = slave; + rt766->regmap = regmap; + + regcache_cache_only(rt766->regmap, true); + + ret = devm_mutex_init(dev, &rt766->disable_irq_lock); + if (ret < 0) { + dev_err(dev, "Failed to initialize mutex\n"); + return ret; + } + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt766->hw_init = false; + rt766->first_hw_init = false; + rt766->fu41_dapm_mute = true; + rt766->fu41_mixer_l_mute = rt766->fu41_mixer_r_mute = false; + rt766->fu36_dapm_mute = true; + rt766->fu36_mixer_l_mute = rt766->fu36_mixer_r_mute = true; + rt766->fu21_dapm_mute = true; + rt766->fu21_mixer_l_mute = rt766->fu21_mixer_r_mute = false; + rt766->fu113_dapm_mute = true; + rt766->fu113_mixer_mute[0] = rt766->fu113_mixer_mute[1] = + rt766->fu113_mixer_mute[2] = rt766->fu113_mixer_mute[3] = true; + + dai_drv = devm_kzalloc(dev, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai), GFP_KERNEL); + if (!dai_drv) { + dev_err(dev, "Failed to allocate memory for DAI driver\n"); + ret = -ENOMEM; + goto _sdw_init_err_; + } + memcpy(dai_drv, rt766_sdca_dai, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai)); + + /* get SDCA function data */ + dev_dbg(dev, "SDCA functions found: %d", slave->sdca_data.num_functions); + for (i = 0; i < slave->sdca_data.num_functions; i++) { + func_data_ptr = devm_kzalloc(dev, sizeof(*func_data_ptr), GFP_KERNEL); + if (!func_data_ptr) { + dev_err(dev, "Failed to allocate memory for function data\n"); + ret = -ENOMEM; + goto _free_dai_drv_; + } + + func_data_ptr->desc = &slave->sdca_data.function[i]; + ret = sdca_parse_function(dev, func_data_ptr); + if (ret) { + devm_kfree(dev, func_data_ptr); + goto _free_dai_drv_; + } + dev_dbg(dev, "Function type=%d, num_entities=%d", + slave->sdca_data.function[i].type, func_data_ptr->num_entities); + + switch (slave->sdca_data.function[i].type) { + case SDCA_FUNCTION_TYPE_UAJ: + rt766->uaj_func_data = func_data_ptr; + /* + * Some machines may only support a subset of the sample rates supported by the codec. + * Therefore, we need to parse the supported sample rates from the DisCo table and + * configure them in the DAI. If the DisCo table does not provide sample rate information, + * we will fall back to the default supported rates defined in the codec driver. + */ + rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 41"); + if (rates) + dai_drv[RT766_DAI_UAJ].playback.rates = rates; + + rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 36"); + if (rates) + dai_drv[RT766_DAI_UAJ].capture.rates = rates; + break; + case SDCA_FUNCTION_TYPE_SMART_AMP: + rt766->sa_func_data = func_data_ptr; + rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 21"); + if (rates) + dai_drv[RT766_DAI_AMP].playback.rates = rates; + break; + case SDCA_FUNCTION_TYPE_SMART_MIC: + rt766->sm_func_data = func_data_ptr; + rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 113"); + if (rates) + dai_drv[RT766_DAI_MIC].capture.rates = rates; + break; + case SDCA_FUNCTION_TYPE_HID: + rt766->hid_func_data = func_data_ptr; + break; + default: + dev_dbg(dev, "Unexpected SDCA function type found: %d", + slave->sdca_data.function[i].type); + } + } + + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt766, dai_drv, ARRAY_SIZE(rt766_sdca_dai)); + if (ret < 0) + goto _free_dai_drv_; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + dev_dbg(dev, "%s\n", __func__); + return 0; + +_free_dai_drv_: + if (dai_drv) + devm_kfree(dev, dai_drv); + +_sdw_init_err_: + return ret; +} + +static int rt766_func_initialize(struct rt766_sdca_priv *rt766, struct sdca_function_data *func_data) +{ + struct device *dev = &rt766->slave->dev; + unsigned int func_status_reg; + unsigned int func_status; + int ret; + + switch (func_data->desc->type) { + case SDCA_FUNCTION_TYPE_UAJ: + func_status_reg = RT766_FUNC_STATUS_REG(UAJ); + break; + case SDCA_FUNCTION_TYPE_SMART_AMP: + func_status_reg = RT766_FUNC_STATUS_REG(AMP); + break; + case SDCA_FUNCTION_TYPE_SMART_MIC: + func_status_reg = RT766_FUNC_STATUS_REG(MIC); + break; + case SDCA_FUNCTION_TYPE_HID: + func_status_reg = RT766_FUNC_STATUS_REG(HID); + break; + default: + dev_dbg(dev, "Unexpected SDCA function type found: %d", + func_data->desc->type); + return -EINVAL; + } + + regmap_read(rt766->regmap, func_status_reg, &func_status); + dev_dbg(dev, "%s, %s func_status=0x%x\n", __func__, func_data->desc->name, func_status); + + if ((func_status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) || (!rt766->first_hw_init)) { + ret = sdca_regmap_write_init(dev, rt766->regmap, func_data); + if (ret) { + dev_err(dev, "%s initialization table update failed\n", func_data->desc->name); + goto _func_init_err_; + } + + regmap_write(rt766->regmap, func_status_reg, + SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION); + } + + return 0; + +_func_init_err_: + dev_err(dev, "%s: %s init writes failed, err=%d", __func__, func_data->desc->name, ret); + return ret; +} + +int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); + unsigned int val; + + rt766->disable_irq = false; + + if (rt766->hw_init) + return 0; + + regcache_cache_only(rt766->regmap, false); + if (rt766->first_hw_init) { + regcache_cache_bypass(rt766->regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + regmap_read(rt766->regmap, RT766_BOND_LATCH_ID, &val); + dev_dbg(&slave->dev, "%s bond ID=0x%x (%s)\n", __func__, val, (val == 0x1) ? "RT767" : "RT766"); + + /* check function status and initialize if needed */ + if (rt766->uaj_func_data) + rt766_func_initialize(rt766, rt766->uaj_func_data); + if (rt766->sa_func_data) + rt766_func_initialize(rt766, rt766->sa_func_data); + if (rt766->sm_func_data) + rt766_func_initialize(rt766, rt766->sm_func_data); + if (rt766->hid_func_data) + rt766_func_initialize(rt766, rt766->hid_func_data); + + if (rt766->first_hw_init) { + regcache_cache_bypass(rt766->regmap, false); + regcache_mark_dirty(rt766->regmap); + } else { + rt766->first_hw_init = true; + } + + /* Mark Slave initialization complete */ + rt766->hw_init = true; + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + + pm_runtime_put_autosuspend(&slave->dev); + + return 0; +} + +MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("SND_SOC_SDCA"); diff --git a/sound/soc/codecs/rt766-sdca.h b/sound/soc/codecs/rt766-sdca.h new file mode 100644 index 000000000000..de4064007eb9 --- /dev/null +++ b/sound/soc/codecs/rt766-sdca.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt766-sdca.h -- RT766 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2026 Realtek Semiconductor Corp. + */ + +#ifndef __RT766_H__ +#define __RT766_H__ + +#include <linux/hid.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <linux/workqueue.h> + +struct rt766_sdca_priv { + struct regmap *regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + bool hw_init; + bool first_hw_init; + struct snd_soc_jack *hs_jack; + struct mutex disable_irq_lock; /* SDCA irq lock protection */ + bool disable_irq; + int jack_type; + bool fu41_dapm_mute; + bool fu41_mixer_l_mute; + bool fu41_mixer_r_mute; + bool fu113_dapm_mute; + bool fu113_mixer_mute[4]; + bool fu21_dapm_mute; + bool fu21_mixer_l_mute; + bool fu21_mixer_r_mute; + bool fu36_dapm_mute; + bool fu36_mixer_l_mute; + bool fu36_mixer_r_mute; + struct sdca_function_data *uaj_func_data; + struct sdca_function_data *sm_func_data; + struct sdca_function_data *sa_func_data; + struct sdca_function_data *hid_func_data; + struct sdca_interrupt_info *irq_info; + struct hid_device *hid; +}; + +/* vendor registers */ +#define RT766_VERSION_ID 0xc404 +#define RT766_DEV_ID1 0xc405 +#define RT766_DEV_ID0 0xc406 +#define RT766_BOND_LATCH_ID 0xc407 + +#define RT766_HP_POWER_STATE 0x1000004 +#define RT766_HP_FSM_CTL2_1 0x100000d + +/* MCU Patch address */ +#define RT766_MCU_PATCH_ADDR1_START 0x10010000 +#define RT766_MCU_PATCH_ADDR1_END 0x10011fff +#define RT766_MCU_PATCH_ADDR2_START 0x10020000 +#define RT766_MCU_PATCH_ADDR2_END 0x10023fff + +/* Buffer address for HID */ +#define RT766_BUF_ADDR_HID1 0x44030000 +#define RT766_BUF_ADDR_HID2 0x44030020 + +/* SDCA (Channel) */ +#define RT766_CH_1 0x01 +#define RT766_CH_2 0x02 +#define RT766_CH_3 0x03 +#define RT766_CH_4 0x04 + +/* RT766 SDCA Control - function number */ +#define RT766_FUNC_NUM_UAJ 0x01 +#define RT766_FUNC_NUM_MIC 0x02 +#define RT766_FUNC_NUM_HID 0x03 +#define RT766_FUNC_NUM_AMP 0x04 + +/* RT766 SDCA entity */ +#define RT766_SDCA_ENT_0 0x00 +#define RT766_SDCA_ENT_HID101 0x01 +#define RT766_SDCA_ENT_GE49 0x49 +#define RT766_SDCA_ENT_USER_FU41 0x05 +#define RT766_SDCA_ENT_USER_FU36 0x0f +#define RT766_SDCA_ENT_USER_FU21 0x03 +#define RT766_SDCA_ENT_USER_FU113 0x30 +#define RT766_SDCA_ENT_PDE23 0x33 +#define RT766_SDCA_ENT_PDE47 0x28 +#define RT766_SDCA_ENT_PDE11 0x2a +#define RT766_SDCA_ENT_PDE34 0x29 +#define RT766_SDCA_ENT_CS41 0x01 +#define RT766_SDCA_ENT_CS36 0x11 +#define RT766_SDCA_ENT_CS113 0x12 +#define RT766_SDCA_ENT_CS21 0x21 +#define RT766_SDCA_ENT_PLATFORM_FU33 0x44 +#define RT766_SDCA_ENT_PPU21 0x04 + +/* sample frequency index */ +#define RT766_SDCA_RATE_44100HZ 0x08 +#define RT766_SDCA_RATE_48000HZ 0x09 +#define RT766_SDCA_RATE_96000HZ 0x0b +#define RT766_SDCA_RATE_192000HZ 0x0d + +/* SDCA Register macros */ +#define RT766_MUTE_REG(func, fu, ch) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_MUTE, RT766_CH_##ch) + +#define RT766_VOLUME_REG(func, fu, ch) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_CHANNEL_VOLUME, RT766_CH_##ch) + +#define RT766_GAIN_REG(func, fu, ch) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_GAIN, RT766_CH_##ch) + +#define RT766_PDE_REQ_REG(func, pde) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_REQUESTED_PS, 0) + +#define RT766_PDE_ACTUAL_REG(func, pde) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_ACTUAL_PS, 0) + +#define RT766_FUNC_STATUS_REG(func) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_0, SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0) + +#define RT766_SDCA_CTL(func, ent, ctl) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##ent, ctl, 0) + +enum { + RT766_AIF1, + RT766_AIF2, + RT766_AIF3, +}; + +enum { + RT766_DAI_UAJ, + RT766_DAI_AMP, + RT766_DAI_MIC, +}; + +int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave); +#endif /* __RT766_H__ */ diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c index 97f56af25577..e51cd4a31894 100644 --- a/sound/soc/codecs/rt9120.c +++ b/sound/soc/codecs/rt9120.c @@ -606,12 +606,19 @@ static int rt9120_runtime_suspend(struct device *dev) static int rt9120_runtime_resume(struct device *dev) { struct rt9120_data *data = dev_get_drvdata(dev); + int ret; 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); + ret = regcache_sync(data->regmap); + if (ret) { + regcache_cache_only(data->regmap, true); + regcache_mark_dirty(data->regmap); + gpiod_set_value(data->pwdnn_gpio, 0); + return ret; + } } return 0; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 59642673b4cb..35df1a8c4c8c 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -56,8 +56,8 @@ static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_CHIP_PLL_CTRL, 0x5000 }, { SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 }, { SGTL5000_CHIP_ANA_STATUS, 0x0000 }, - { SGTL5000_CHIP_SHORT_CTRL, 0x0000 }, { SGTL5000_CHIP_ANA_TEST2, 0x0000 }, + { SGTL5000_CHIP_SHORT_CTRL, 0x0000 }, { SGTL5000_DAP_CTRL, 0x0000 }, { SGTL5000_DAP_PEQ, 0x0000 }, { SGTL5000_DAP_BASS_ENHANCE, 0x0040 }, diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 2e08fde3989c..4ecbbac93258 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -5,6 +5,7 @@ * Copyright 2009-2014 Analog Devices Inc. */ +#include <linux/cleanup.h> #include <linux/crc32.h> #include <linux/firmware.h> #include <linux/kernel.h> @@ -135,7 +136,7 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, uint8_t *data; int ret = 0; - mutex_lock(&sigmadsp->lock); + guard(mutex)(&sigmadsp->lock); data = ucontrol->value.bytes.data; @@ -148,8 +149,6 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, ctrl->cached = true; } - mutex_unlock(&sigmadsp->lock); - return ret; } @@ -160,7 +159,7 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); int ret = 0; - mutex_lock(&sigmadsp->lock); + guard(mutex)(&sigmadsp->lock); if (!ctrl->cached) { ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, @@ -174,8 +173,6 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, ctrl->num_bytes); } - mutex_unlock(&sigmadsp->lock); - return ret; } @@ -487,7 +484,7 @@ static void devm_sigmadsp_release(struct device *dev, void *res) static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) { const struct sigma_firmware_header *ssfw_head; - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int ret; u32 crc; @@ -495,7 +492,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) ret = request_firmware(&fw, name, sigmadsp->dev); if (ret) { pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); - goto done; + return ret; } /* then verify the header */ @@ -509,13 +506,13 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) */ if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); - goto done; + return -EINVAL; } ssfw_head = (void *)fw->data; if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); - goto done; + return -EINVAL; } crc = crc32(0, fw->data + sizeof(*ssfw_head), @@ -524,7 +521,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) if (crc != le32_to_cpu(ssfw_head->crc)) { dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", le32_to_cpu(ssfw_head->crc), crc); - goto done; + return -EINVAL; } switch (ssfw_head->version) { @@ -545,9 +542,6 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) if (ret) sigmadsp_firmware_release(sigmadsp); -done: - release_firmware(fw); - return ret; } @@ -677,10 +671,10 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, return; changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active); if (active && changed > 0) { - mutex_lock(&sigmadsp->lock); - if (ctrl->cached) - sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); - mutex_unlock(&sigmadsp->lock); + scoped_guard(mutex, &sigmadsp->lock) { + if (ctrl->cached) + sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); + } } } diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c index 7fce60de5e5f..15758eb13f8c 100644 --- a/sound/soc/codecs/sma1303.c +++ b/sound/soc/codecs/sma1303.c @@ -1777,6 +1777,8 @@ static void sma1303_i2c_remove(struct i2c_client *client) struct sma1303_priv *sma1303 = (struct sma1303_priv *) i2c_get_clientdata(client); + if (sma1303->attr_grp) + sysfs_remove_group(sma1303->kobj, sma1303->attr_grp); cancel_delayed_work_sync(&sma1303->check_fault_work); } diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c index c52fe95b30c6..adb369a29b9d 100644 --- a/sound/soc/codecs/sma1307.c +++ b/sound/soc/codecs/sma1307.c @@ -1690,7 +1690,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int size, offset, num_mode; int ret; @@ -1703,22 +1703,18 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil return; } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) { dev_err(sma1307->dev, "%s: Invalid file\n", __func__); - release_firmware(fw); sma1307->set.status = false; return; } int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL); if (!data) { - release_firmware(fw); sma1307->set.status = false; return; } size = fw->size >> 2; memcpy(data, fw->data, fw->size); - release_firmware(fw); - /* HEADER */ sma1307->set.header_size = SMA1307_SETTING_HEADER_SIZE; sma1307->set.checksum = data[sma1307->set.header_size - 2]; diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 652c6e3a9e63..d6de739212f9 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/pm.h> @@ -269,9 +270,9 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; unsigned int cfud, val; - int i, ret = 0; + int i; - mutex_lock(&sta32x->coeff_lock); + guard(mutex)(&sta32x->coeff_lock); /* preserve reserved bits in STA32X_CFUD */ regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); @@ -283,24 +284,20 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, regmap_write(sta32x->regmap, STA32X_CFUD, cfud); regmap_write(sta32x->regmap, STA32X_CFADDR2, index); - if (numcoef == 1) { + if (numcoef == 1) regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04); - } else if (numcoef == 5) { + else if (numcoef == 5) regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08); - } else { - ret = -EINVAL; - goto exit_unlock; - } + else + return -EINVAL; + for (i = 0; i < 3 * numcoef; i++) { regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val); ucontrol->value.bytes.data[i] = val; } -exit_unlock: - mutex_unlock(&sta32x->coeff_lock); - - return ret; + return 0; } static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index 99c7f7ac807b..2ba35076732b 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -306,9 +307,9 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; unsigned int cfud, val; - int i, ret = 0; + int i; - mutex_lock(&sta350->coeff_lock); + guard(mutex)(&sta350->coeff_lock); /* preserve reserved bits in STA350_CFUD */ regmap_read(sta350->regmap, STA350_CFUD, &cfud); @@ -320,24 +321,19 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, regmap_write(sta350->regmap, STA350_CFUD, cfud); regmap_write(sta350->regmap, STA350_CFADDR2, index); - if (numcoef == 1) { + if (numcoef == 1) regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); - } else if (numcoef == 5) { + else if (numcoef == 5) regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); - } else { - ret = -EINVAL; - goto exit_unlock; - } + else + return -EINVAL; for (i = 0; i < 3 * numcoef; i++) { regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); ucontrol->value.bytes.data[i] = val; } -exit_unlock: - mutex_unlock(&sta350->coeff_lock); - - return ret; + return 0; } static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c index 4ab15be69f3a..b7eaf4795d8b 100644 --- a/sound/soc/codecs/sti-sas.c +++ b/sound/soc/codecs/sti-sas.c @@ -44,8 +44,8 @@ enum { }; static const struct reg_default stih407_sas_reg_defaults[] = { - { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, + { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, }; struct sti_dac_audio { diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c index ace06f5ab58c..c062065a807d 100644 --- a/sound/soc/codecs/tac5xx2-sdw.c +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -88,6 +88,9 @@ #define TAC_FW_FILE_HDR 20 #define TAC_MAX_FW_CHUNKS 512 +#define TAC_UAJ_PREP_CONNECTED 0xff +#define TAC_UAJ_PREP_DISCONNECTED 0xdf + struct tac_fw_hdr { u32 size; u32 version_offset; @@ -138,6 +141,7 @@ struct tac5xx2_prv { bool hw_init; bool first_hw_init_done; u32 part_id; + u32 rev_id; struct snd_soc_jack *hs_jack; int jack_type; /* Custom fw binary. UMP File Download is not used. */ @@ -261,7 +265,6 @@ static const struct reg_default tac_reg_default[] = { {TAC_REG_SDW(0, 0, 0x78), 0x0}, {TAC_REG_SDW(0, 0, 0x7b), 0x0}, {TAC_REG_SDW(0, 0, 0x7c), 0xd0}, - {TAC_REG_SDW(0, 0, 0x7d), 0x0}, {TAC_REG_SDW(0, 0, 0x7e), 0x0}, {TAC_REG_SDW(0, 1, 0x1), 0x0}, {TAC_REG_SDW(0, 1, 0x2), 0x0}, @@ -507,18 +510,6 @@ static const struct regmap_config tac_regmap = { .use_single_write = true, }; -/* Check if device has DSP algo that needs status monitoring */ -static bool tac_has_dsp_algo(struct tac5xx2_prv *tac_dev) -{ - switch (tac_dev->part_id) { - case 0x5682: - case 0x2883: - return true; - default: - return false; - } -} - /* Check if device has UAJ (Universal Audio Jack) support */ static bool tac_has_uaj_support(struct tac5xx2_prv *tac_dev) { @@ -800,7 +791,7 @@ static int tac_sdw_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity, + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0); if (ret) dev_err(tac_dev->dev, "failed to transition func %d, pde %d from PS3 -> PS0, err=%d\n", @@ -847,7 +838,7 @@ static int tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream, return ret; } - ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3, NULL, 0); if (ret) @@ -942,6 +933,7 @@ end_btn_det: static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) { int val, ret; + u8 jack_prep; ret = regmap_read(tac_dev->regmap, SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35, @@ -951,6 +943,8 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) return ret; } + jack_prep = TAC_UAJ_PREP_CONNECTED; + switch (val) { case 4: tac_dev->jack_type = SND_JACK_MICROPHONE; @@ -964,6 +958,7 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) case 0: default: tac_dev->jack_type = 0; + jack_prep = TAC_UAJ_PREP_DISCONNECTED; break; } @@ -973,26 +968,44 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) if (ret) dev_err(tac_dev->dev, "Failed to update the jack type to device"); + /* + * When uaj is uplugged and booted, we end up with the channel prepare + * timeout error for the uaj ports. This writes allows the channel prepare + * to succeed when the uaj is not plugged in. + */ + if (tac_dev->rev_id >= 0x30) { + ret = regmap_write(tac_dev->regmap, TAC_REG_SDW(0, 3, 127), jack_prep); + if (ret) + dev_warn(tac_dev->dev, "Failed to write jack_prep register: %d\n", ret); + } + return 0; } static int tac5xx2_jack_init(struct tac5xx2_prv *tac_dev) { + u32 jd_int_mask, hid_int_mask; int ret = 0; + if (tac_dev->rev_id >= 0x30) { + jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_12; + hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_17; + } else { + jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_11; + hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_16; + } + if (!tac_dev->hs_jack) goto disable_interrupts; - ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_11); + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, jd_int_mask); if (ret) { dev_err(tac_dev->dev, "Failed to register jack detection interrupt: %d\n", ret); goto disable_interrupts; } - ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, - SDW_SCP_SDCA_INTMASK_SDCA_16); + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, hid_int_mask); if (ret) { dev_err(tac_dev->dev, "Failed to register for button detect interrupt: %d\n", ret); @@ -1051,6 +1064,7 @@ static int tac_interrupt_callback(struct sdw_slave *slave, unsigned int sdca_int2, sdca_int3, jack_report_mask = 0; struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev); struct device *dev = &slave->dev; + u32 headset_detect_chk, hid_detect_chk; int btn_type = 0; int ret = 0; @@ -1081,14 +1095,22 @@ static int tac_interrupt_callback(struct sdw_slave *slave, dev_dbg(dev, "SDCA_INT2: 0x%02x, SDCA_INT3: 0x%02x\n", sdca_int2, sdca_int3); - if (sdca_int2 & SDW_SCP_SDCA_INT_SDCA_11) { + if (tac_dev->rev_id >= 0x30) { + headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_12; + hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_17; + } else { + headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_11; + hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_16; + } + + if (sdca_int2 & headset_detect_chk) { ret = tac5xx2_sdca_headset_detect(tac_dev); if (ret < 0) goto clear; jack_report_mask |= SND_JACK_HEADSET; } - if (sdca_int3 & SDW_SCP_SDCA_INT_SDCA_16) { + if (sdca_int3 & hid_detect_chk) { btn_type = tac5xx2_sdca_button_detect(tac_dev); if (btn_type < 0) btn_type = 0; @@ -1695,6 +1717,15 @@ static int tac_io_init(struct device *dev, struct sdw_slave *slave, bool first) goto io_init_err; } + if (!tac_dev->rev_id) { + ret = regmap_read(tac_dev->regmap, TAC_REV_ID, &tac_dev->rev_id); + if (ret) { + dev_err(tac_dev->dev, "failed to rev id, err=%d\n", ret); + goto io_init_err; + } + dev_dbg(tac_dev->dev, "detected rev_id 0x%x", tac_dev->rev_id); + } + if (tac_dev->fw_files && tac_dev->fw_file_cnt > 0) { ret = tac_download_fw_to_hw(tac_dev); if (ret) { @@ -1940,7 +1971,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, "failed to allocate %s function data", func_name); function_data->desc = &peripheral->sdca_data.function[i]; - ret = sdca_parse_function(dev, peripheral, function_data); + ret = sdca_parse_function(dev, function_data); if (!ret) *func_ptr = function_data; else @@ -1959,6 +1990,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, tac_dev->hw_init = false; tac_dev->first_hw_init_done = false; tac_dev->part_id = id->part_id; + tac_dev->rev_id = 0x0; dev_set_drvdata(dev, tac_dev); regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev, peripheral, @@ -1972,17 +2004,13 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, tac_dev->jack_type = 0; init_completion(&tac_dev->fw_caching_complete); - if (tac_has_dsp_algo(tac_dev)) { - tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, - sizeof(tac_dev->fw_binaryname)); + tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, + sizeof(tac_dev->fw_binaryname)); - ret = tac_load_and_cache_firmware_async(tac_dev); - if (ret) { - complete_all(&tac_dev->fw_caching_complete); - dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); - } - } else { + ret = tac_load_and_cache_firmware_async(tac_dev); + if (ret) { complete_all(&tac_dev->fw_caching_complete); + dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); } ret = tac_init(tac_dev); diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 1a7650b9b2a7..be2c9a0fa529 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -31,25 +31,25 @@ static const struct reg_default tas2552_reg_defs[] = { {TAS2552_CFG_1, 0x22}, + {TAS2552_CFG_2, 0xef}, {TAS2552_CFG_3, 0x80}, {TAS2552_DOUT, 0x00}, - {TAS2552_OUTPUT_DATA, 0xc0}, - {TAS2552_PDM_CFG, 0x01}, - {TAS2552_PGA_GAIN, 0x00}, - {TAS2552_BOOST_APT_CTRL, 0x0f}, - {TAS2552_RESERVED_0D, 0xbe}, - {TAS2552_LIMIT_RATE_HYS, 0x08}, - {TAS2552_CFG_2, 0xef}, {TAS2552_SER_CTRL_1, 0x00}, {TAS2552_SER_CTRL_2, 0x00}, + {TAS2552_OUTPUT_DATA, 0xc0}, {TAS2552_PLL_CTRL_1, 0x10}, {TAS2552_PLL_CTRL_2, 0x00}, {TAS2552_PLL_CTRL_3, 0x00}, {TAS2552_BTIP, 0x8f}, {TAS2552_BTS_CTRL, 0x80}, + {TAS2552_RESERVED_0D, 0xbe}, + {TAS2552_LIMIT_RATE_HYS, 0x08}, {TAS2552_LIMIT_RELEASE, 0x04}, {TAS2552_LIMIT_INT_COUNT, 0x00}, + {TAS2552_PDM_CFG, 0x01}, + {TAS2552_PGA_GAIN, 0x00}, {TAS2552_EDGE_RATE_CTRL, 0x40}, + {TAS2552_BOOST_APT_CTRL, 0x0f}, {TAS2552_VBAT_DATA, 0x00}, }; @@ -495,13 +495,21 @@ static int tas2552_runtime_suspend(struct device *dev) static int tas2552_runtime_resume(struct device *dev) { struct tas2552_data *tas2552 = dev_get_drvdata(dev); + int ret; gpiod_set_value_cansleep(tas2552->enable_gpio, 1); tas2552_sw_shutdown(tas2552, 0); regcache_cache_only(tas2552->regmap, false); - regcache_sync(tas2552->regmap); + ret = regcache_sync(tas2552->regmap); + if (ret) { + regcache_cache_only(tas2552->regmap, true); + regcache_mark_dirty(tas2552->regmap); + tas2552_sw_shutdown(tas2552, 1); + gpiod_set_value_cansleep(tas2552->enable_gpio, 0); + return ret; + } return 0; } diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index b11b998aa32a..f9c65c9f6d0a 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -897,13 +897,13 @@ static const struct reg_default tas2764_reg_defaults[] = { { TAS2764_PAGE, 0x00 }, { TAS2764_SW_RST, 0x00 }, { TAS2764_PWR_CTRL, 0x1a }, - { TAS2764_DVC, 0x00 }, { TAS2764_CHNL_0, 0x28 }, { TAS2764_TDM_CFG0, 0x09 }, { TAS2764_TDM_CFG1, 0x02 }, { TAS2764_TDM_CFG2, 0x0a }, { TAS2764_TDM_CFG3, 0x10 }, { TAS2764_TDM_CFG5, 0x42 }, + { TAS2764_DVC, 0x00 }, { TAS2764_INT_CLK_CFG, 0x19 }, }; diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index 1ec1c076204f..f1fce4305fa1 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -527,13 +527,13 @@ static const struct reg_default tas2780_reg_defaults[] = { { TAS2780_PAGE, 0x00 }, { TAS2780_SW_RST, 0x00 }, { TAS2780_PWR_CTRL, 0x1a }, - { TAS2780_DVC, 0x00 }, { TAS2780_CHNL_0, 0x00 }, { TAS2780_TDM_CFG0, 0x09 }, { TAS2780_TDM_CFG1, 0x02 }, { TAS2780_TDM_CFG2, 0x0a }, { TAS2780_TDM_CFG3, 0x10 }, { TAS2780_TDM_CFG5, 0x42 }, + { TAS2780_DVC, 0x00 }, }; static const struct regmap_range_cfg tas2780_regmap_ranges[] = { diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c index e24d56a14cfd..79b5f5b04c74 100644 --- a/sound/soc/codecs/tas2781-comlib-i2c.c +++ b/sound/soc/codecs/tas2781-comlib-i2c.c @@ -6,6 +6,7 @@ // // Author: Shenghao Ding <shenghao-ding@ti.com> +#include <linux/cleanup.h> #include <linux/crc8.h> #include <linux/firmware.h> #include <linux/gpio/consumer.h> @@ -342,7 +343,7 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, /* Codec Lock Hold to ensure that codec_probe and firmware parsing and * loading do not simultaneously execute. */ - mutex_lock(&tas_priv->codec_lock); + guard(mutex)(&tas_priv->codec_lock); if (tas_priv->name_prefix) scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", @@ -360,8 +361,6 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", ret); - /* Codec Lock Release*/ - mutex_unlock(&tas_priv->codec_lock); return ret; } EXPORT_SYMBOL_GPL(tascodec_init); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index bfabff583d57..2b9efccf1a0b 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -2253,7 +2253,7 @@ int tas2781_load_calibration(void *context, char *file_name, { struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); - const struct firmware *fw_entry = NULL; + const struct firmware *fw_entry __free(firmware) = NULL; struct tasdevice_fw *tas_fmw; struct firmware fmw; int offset = 0; @@ -2263,60 +2263,50 @@ int tas2781_load_calibration(void *context, char *file_name, if (ret) { dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", __func__, file_name); - goto out; + return ret; } if (!fw_entry->size) { dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", __func__, (unsigned long)fw_entry->size); - ret = -EINVAL; - goto out; + return -EINVAL; } fmw.size = fw_entry->size; fmw.data = fw_entry->data; tas_fmw = tasdev->cali_data_fmw = kzalloc_obj(struct tasdevice_fw); - if (!tasdev->cali_data_fmw) { - ret = -ENOMEM; - goto out; - } + if (!tasdev->cali_data_fmw) + return -ENOMEM; + tas_fmw->dev = tas_priv->dev; offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); if (offset == -EINVAL) { dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); - ret = offset; - goto out; + return -EINVAL; } offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); if (offset == -EINVAL) { dev_err(tas_priv->dev, "%s: fw_parse_variable_header_cal EXIT!\n", __func__); - ret = offset; - goto out; + return -EINVAL; } offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); if (offset < 0) { dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); - ret = offset; - goto out; + return offset; } offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); if (offset < 0) { dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); - ret = offset; - goto out; + return offset; } offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); if (offset < 0) { dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); - ret = offset; - goto out; + return offset; } -out: - release_firmware(fw_entry); - - return ret; + return 0; } EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, "SND_SOC_TAS2781_FMWLIB"); @@ -2409,7 +2399,7 @@ static int tasdevice_dspfw_ready(const struct firmware *fmw, int tasdevice_dsp_parser(void *context) { struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; - const struct firmware *fw_entry; + const struct firmware *fw_entry __free(firmware) = NULL; int ret; ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, @@ -2417,15 +2407,10 @@ int tasdevice_dsp_parser(void *context) if (ret) { dev_err(tas_priv->dev, "%s: load %s error\n", __func__, tas_priv->coef_binaryname); - goto out; + return ret; } - ret = tasdevice_dspfw_ready(fw_entry, tas_priv); - release_firmware(fw_entry); - fw_entry = NULL; - -out: - return ret; + return tasdevice_dspfw_ready(fw_entry, tas_priv); } EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, "SND_SOC_TAS2781_FMWLIB"); @@ -2807,11 +2792,12 @@ out: } EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, "SND_SOC_TAS2781_FMWLIB"); -void tasdevice_tuning_switch(void *context, int state) +void tasdevice_tuning_switch(void *context, int state, bool is_cap) { struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; struct tasdevice_fw *tas_fmw = tas_priv->fmw; - int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + int profile_cfg_id = is_cap ? tas_priv->rcabin.capture_profile_id : + tas_priv->rcabin.profile_cfg_id; /* * Only RCA-based Playback can still work with no dsp program running @@ -2828,7 +2814,6 @@ void tasdevice_tuning_switch(void *context, int state) if (state == 0) { if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) { /* dsp mode or tuning mode */ - profile_cfg_id = tas_priv->rcabin.profile_cfg_id; tasdevice_select_tuningprm_cfg(tas_priv, tas_priv->cur_prog, tas_priv->cur_conf, profile_cfg_id); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 95d875c44b91..6f349f88f194 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -13,6 +13,7 @@ // Author: Kevin Lu <kevin-lu@ti.com> // +#include <linux/cleanup.h> #include <linux/crc8.h> #include <linux/firmware.h> #include <linux/gpio/consumer.h> @@ -617,6 +618,7 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); + int i; guard(mutex)(&priv->codec_lock); if (priv->chip_id == TAS2563) @@ -624,6 +626,14 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, else tas2781_calib_stop_put(priv); + /* + * Set reloading-firmware flag after calibration, the flag will work + * during next playback, then set to the program id after reloading + * firmware. + */ + for (i = 0; i < priv->ndev; i++) + priv->tasdevice[i].cur_prog = -1; + return 1; } @@ -843,12 +853,12 @@ static int tasdevice_digital_gain_get( unsigned char data[4]; int ret; - mutex_lock(&tas_dev->codec_lock); + guard(mutex)(&tas_dev->codec_lock); /* Read the primary device */ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); if (ret) { dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); - goto out; + return ret; } target = get_unaligned_be32(&data[0]); @@ -868,8 +878,7 @@ static int tasdevice_digital_gain_get( /* find out the member same as or closer to the current volume */ ucontrol->value.integer.value[0] = abs(target - ar_l) <= abs(target - ar_r) ? l : r; -out: - mutex_unlock(&tas_dev->codec_lock); + return 0; } @@ -882,29 +891,26 @@ static int tasdevice_digital_gain_put( struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); int vol = ucontrol->value.integer.value[0]; - int status = 0, max = mc->max, rc = 1; + int status = 0, max = mc->max; int i, ret; unsigned int reg = mc->reg; unsigned int volrd, volwr; unsigned char data[4]; vol = clamp(vol, 0, max); - mutex_lock(&tas_dev->codec_lock); + guard(mutex)(&tas_dev->codec_lock); /* Read the primary device */ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); if (ret) { dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); - rc = -1; - goto out; + return -1; } volrd = get_unaligned_be32(&data[0]); volwr = get_unaligned_be32(tas_dev->dvc_tlv_table[vol]); - if (volrd == volwr) { - rc = 0; - goto out; - } + if (volrd == volwr) + return 0; for (i = 0; i < tas_dev->ndev; i++) { ret = tasdevice_dev_bulk_write(tas_dev, i, reg, @@ -918,10 +924,9 @@ static int tasdevice_digital_gain_put( } if (status) - rc = -1; -out: - mutex_unlock(&tas_dev->codec_lock); - return rc; + return -1; + + return 1; } static const struct snd_kcontrol_new tasdevice_cali_controls[] = { @@ -995,6 +1000,57 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, return ret; } +/** + * tasdevice_set_capture_profile_id - Set runtime capture profile index via + * ALSA control + * @kcontrol: ALSA kcontrol handle that triggers this operation + * @ucontrol: User space control value carrying the new profile index + * + * This mixer control handler validates the user-provided capture profile ID + * against the maximum valid index parsed from the loaded DSP firmware, + * then updates the runtime stored capture profile ID only if the new value + * differs from the current active one. It will immediately return -EINVAL + * if the submitted profile ID falls outside the valid range, including the + * edge case that no valid configuration blocks are detected in firmware. + * + * No actual DSP register write is performed in this handler. The updated + * profile ID will be applied to the hardware when the next ALSA capture + * stream starts up. Caller does not need to take extra codec lock here, + * as the ALSA control core already guarantees serialized execution. + * + * Return: 1 if profile ID value was changed, 0 if no modification needed, + * -EINVAL if the input profile ID is out of valid range + */ +static int tasdevice_set_capture_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int user_prof_id = ucontrol->value.integer.value[0]; + unsigned int max_valid_id; + int ret = 0; + + /* + * Align valid range with the bound defined in + * tasdevice_info_profile() + */ + max_valid_id = tas_priv->rcabin.ncfgs - 1; + + /* + * Reject invalid input including zero total configuration edge + * case + */ + if (tas_priv->rcabin.ncfgs == 0 || user_prof_id > max_valid_id) + return -EINVAL; + + if (tas_priv->rcabin.capture_profile_id != user_prof_id) { + tas_priv->rcabin.capture_profile_id = user_prof_id; + ret = 1; + } + + return ret; +} + static int tasdevice_info_active_num(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1075,6 +1131,41 @@ static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, return 0; } +/** + * tasdevice_get_capture_profile_id - Report current active capture profile + * ID to user space + * @kcontrol: ALSA kcontrol structure passed from ALSA core + * @ucontrol: User-space control element value buffer to write the result back + * + * This function ensures the returned profile ID is always clamped inside the + * valid range advertised by the info callback, preventing accidental invalid + * values from being exposed to applications even if internal driver state is + * temporarily inconsistent. + * + * Returns 0 on successful fill of the control value, no error conditions + * are defined for this getter callback. + */ +static int tasdevice_get_capture_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int max_valid_id, current_prof_id; + + max_valid_id = tas_priv->rcabin.ncfgs > 0 ? + (tas_priv->rcabin.ncfgs - 1U) : 0; + + /* + * Cast current profile id to unsigned to match type with max_valid_id, + * avoid signedness mismatch; + */ + current_prof_id = (unsigned int)tas_priv->rcabin.capture_profile_id; + /* Prevent underflow when there are no loaded capture profiles. */ + ucontrol->value.integer.value[0] = min(current_prof_id, max_valid_id); + + return 0; +} + static int tasdevice_get_chip_id(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1117,6 +1208,41 @@ static int tasdevice_create_control(struct tasdevice_priv *tas_priv) ret = snd_soc_add_component_controls(tas_priv->codec, prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + mix_index = 0; + switch (tas_priv->chip_id) { + case TAS2563: + case TAS2568: + case TAS2570: + case TAS2572: + case TAS2573: + case TAS2574: + case TAS2781: + prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(prof_ctrls[0]), GFP_KERNEL); + if (!prof_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the capture profile */ + name = devm_kstrdup(tas_priv->dev, "Speaker Capture Profile Id", + GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + prof_ctrls[mix_index].name = name; + prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + prof_ctrls[mix_index].info = tasdevice_info_profile; + prof_ctrls[mix_index].get = tasdevice_get_capture_profile_id; + prof_ctrls[mix_index].put = tasdevice_set_capture_profile_id; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, + prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + break; + } + out: return ret; } @@ -1330,6 +1456,43 @@ static void alpa_cali_update(struct bulk_reg_val *p, p->val_len = 4; } +static int create_tas2781_cali_start_ktrl(struct tasdevice_priv + *priv, struct snd_kcontrol_new *cali_ctrl) +{ + struct soc_bytes_ext *ext_cali_start; + char *cali_start_name; + + ext_cali_start = devm_kzalloc(priv->dev, + sizeof(*ext_cali_start), GFP_KERNEL); + if (!ext_cali_start) + return -ENOMEM; + + cali_start_name = devm_kstrdup(priv->dev, + "Calibration Start", GFP_KERNEL); + if (!cali_start_name) + return -ENOMEM; + /* + * package structure for tas2781 ftc start: + * Pkg len (1 byte) + * Reg id (1 byte, constant 'r') + * book, page, register for pilot threshold, pilot tone + * and sine gain (12 bytes) + * for (i = 0; i < Device-Sum; i++) { + * Device #i index_info (1 byte) + * Sine gain for Device #i (8 bytes) + * } + */ + ext_cali_start->max = 14 + priv->ndev * 9; + cali_ctrl->name = cali_start_name; + cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + cali_ctrl->info = snd_soc_bytes_info_ext; + cali_ctrl->put = tas2781_calib_start_put; + cali_ctrl->get = tasdev_nop_get; + cali_ctrl->private_value = (unsigned long)ext_cali_start; + + return 0; +} + static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) { struct calidata *cali_data = &priv->cali_data; @@ -1448,37 +1611,11 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) */ cali_data->data[0] = 0xff; if (priv->chip_id == TAS2781) { - struct soc_bytes_ext *ext_cali_start; - char *cali_start_name; - - ext_cali_start = devm_kzalloc(priv->dev, - sizeof(*ext_cali_start), GFP_KERNEL); - if (!ext_cali_start) - return -ENOMEM; - - cali_start_name = devm_kstrdup(priv->dev, - "Calibration Start", GFP_KERNEL); - if (!cali_start_name) - return -ENOMEM; - /* - * package structure for tas2781 ftc start: - * Pkg len (1 byte) - * Reg id (1 byte, constant 'r') - * book, page, register for pilot threshold, pilot tone - * and sine gain (12 bytes) - * for (i = 0; i < Device-Sum; i++) { - * Device #i index_info (1 byte) - * Sine gain for Device #i (8 bytes) - * } - */ - ext_cali_start->max = 14 + priv->ndev * 9; - cali_ctrls[i].name = cali_start_name; - cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - cali_ctrls[i].info = snd_soc_bytes_info_ext; - cali_ctrls[i].put = tas2781_calib_start_put; - cali_ctrls[i].get = tasdev_nop_get; - cali_ctrls[i].private_value = (unsigned long)ext_cali_start; + rc = create_tas2781_cali_start_ktrl(priv, &cali_ctrls[i]); + if (rc != 0) + return rc; i++; + } return snd_soc_add_component_controls(priv->codec, cali_ctrls, @@ -1765,13 +1902,25 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); int state = 0; - /* Codec Lock Hold */ - mutex_lock(&tas_priv->codec_lock); + guard(mutex)(&tas_priv->codec_lock); if (event == SND_SOC_DAPM_PRE_PMD) state = 1; - tasdevice_tuning_switch(tas_priv, state); - /* Codec Lock Release*/ - mutex_unlock(&tas_priv->codec_lock); + tasdevice_tuning_switch(tas_priv, state, false); + + return 0; +} + +static int tasdevice_capture_dapm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int state = 0; + + guard(mutex)(&tas_priv->codec_lock); + if (event == SND_SOC_DAPM_PRE_PMD) + state = 1; + tasdevice_tuning_switch(tas_priv, state, true); return 0; } @@ -1779,7 +1928,7 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, - 0, 0, tasdevice_dapm_event, + 0, 0, tasdevice_capture_dapm_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), SND_SOC_DAPM_OUTPUT("OUT"), diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 3d0b116544cc..caf8fe1bf4db 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -12,6 +12,7 @@ // Author: Baojun Xu <baojun.xu@ti.com> // Author: Kevin Lu <kevin-lu@ti.com> +#include <linux/cleanup.h> #include <linux/unaligned.h> #include <linux/crc32.h> #include <linux/efi.h> @@ -106,7 +107,6 @@ struct tas2783_prv { static const struct reg_default tas2783_reg_default[] = { {TAS2783_AMP_LEVEL, 0x28}, - {TASDEV_REG_SDW(0, 0, 0x03), 0x28}, {TASDEV_REG_SDW(0, 0, 0x04), 0x21}, {TASDEV_REG_SDW(0, 0, 0x05), 0x41}, {TASDEV_REG_SDW(0, 0, 0x06), 0x00}, @@ -142,6 +142,7 @@ static const struct reg_default tas2783_reg_default[] = { {TASDEV_REG_SDW(0, 0, 0x41), 0x14}, {TASDEV_REG_SDW(0, 0, 0x5c), 0x19}, {TASDEV_REG_SDW(0, 0, 0x5d), 0x80}, + {TASDEV_REG_SDW(0, 0, 0x60), 0x21}, {TASDEV_REG_SDW(0, 0, 0x63), 0x48}, {TASDEV_REG_SDW(0, 0, 0x65), 0x08}, {TASDEV_REG_SDW(0, 0, 0x66), 0xb2}, @@ -157,7 +158,6 @@ static const struct reg_default tas2783_reg_default[] = { {TASDEV_REG_SDW(0, 0, 0x73), 0x08}, {TASDEV_REG_SDW(0, 0, 0x75), 0xe0}, {TASDEV_REG_SDW(0, 0, 0x7a), 0x60}, - {TASDEV_REG_SDW(0, 0, 0x60), 0x21}, {TASDEV_REG_SDW(0, 1, 0x02), 0x00}, {TASDEV_REG_SDW(0, 1, 0x17), 0xc0}, {TASDEV_REG_SDW(0, 1, 0x19), 0x60}, @@ -176,63 +176,44 @@ static const struct reg_default tas2783_reg_default[] = { {TASDEV_REG_SDW(0, 0xfd, 0x39), 0x00}, {TASDEV_REG_SDW(0, 0xfd, 0x3e), 0x00}, {TASDEV_REG_SDW(0, 0xfd, 0x45), 0x00}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x02, 1), 0x9c00}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 0), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x0b, 1), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 1), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0), 0x3}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x04, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x04, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x04, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 1), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 2), 0x0}, @@ -242,19 +223,60 @@ static const struct reg_default tas2783_reg_default[] = { {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 6), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 7), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0), 0x3}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 1), 0x0}, @@ -264,6 +286,14 @@ static const struct reg_default tas2783_reg_default[] = { {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 5), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 6), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 7), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 8), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 9), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xa), 0x0}, @@ -272,36 +302,6 @@ static const struct reg_default tas2783_reg_default[] = { {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xd), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xe), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xf), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0), 0x3}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0), 0x3}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23, 0x10, 0), 0x0}, }; static const struct reg_sequence tas2783_init_seq[] = { @@ -495,6 +495,56 @@ static bool tas2783_readable_register(struct device *dev, unsigned int reg) return tas2783_sdca_mbq_size(dev, reg) > 0; } +static bool tas2783_writeable_register(struct device *dev, unsigned int reg) +{ + /* + * The Latency Control of every Entity, together with the Power Domain + * actual state and the protection status, is read-only. They are + * listed in tas2783_reg_default[] with a placeholder value, so without + * this a regcache_sync() would try to write them back and the + * peripheral would reject the transaction, aborting the sync. + */ + switch (reg) { + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU21, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU23, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU26, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x07, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS24, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS21, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS25, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS26, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS28, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PDE23, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_UDMPU23, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x05, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x11, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU21, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU26, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT21, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT29, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT26, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT28, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT24, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT23, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT25, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT28, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MU26, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT127, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU127, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS127, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x04, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x04, 0): + return false; + + default: + return tas2783_sdca_mbq_size(dev, reg) > 0; + } +} + static bool tas2783_volatile_register(struct device *dev, u32 reg) { switch (reg) { @@ -516,6 +566,7 @@ static const struct regmap_config tas_regmap = { .reg_bits = 32, .val_bits = 8, .readable_reg = tas2783_readable_register, + .writeable_reg = tas2783_writeable_register, .volatile_reg = tas2783_volatile_register, .reg_defaults = tas2783_reg_default, .num_reg_defaults = ARRAY_SIZE(tas2783_reg_default), @@ -693,12 +744,12 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev) return 0; } - mutex_lock(&tas_dev->calib_lock); - ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, - tas_dev->cali_data.read_sz); - if (!ret) - tas2783_set_calib_params_to_device(tas_dev, tmp_val); - mutex_unlock(&tas_dev->calib_lock); + scoped_guard(mutex, &tas_dev->calib_lock) { + ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, + tas_dev->cali_data.read_sz); + if (!ret) + tas2783_set_calib_params_to_device(tas_dev, tmp_val); + } return ret; } @@ -927,22 +978,23 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(tas_dev->dev, "clear latch failed, err=%d", ret); - mutex_lock(&tas_dev->pde_lock); - /* - * Sometimes, there is error returned during power on. - * So added retry logic to ensure power on so that - * port prepare succeeds - */ - do { - ret = regmap_write(tas_dev->regmap, - SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, - TAS2783_SDCA_CTL_REQ_POW_STATE, 0), - TAS2783_SDCA_POW_STATE_ON); - if (!ret) - break; - usleep_range(2000, 2200); - } while (retry--); - mutex_unlock(&tas_dev->pde_lock); + scoped_guard(mutex, &tas_dev->pde_lock) { + /* + * Sometimes, there is error returned during power on. + * So added retry logic to ensure power on so that + * port prepare succeeds + */ + do { + ret = regmap_write(tas_dev->regmap, + SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, + TAS2783_SDCA_CTL_REQ_POW_STATE, 0), + TAS2783_SDCA_POW_STATE_ON); + if (!ret) + break; + usleep_range(2000, 2200); + } while (retry--); + } + if (ret) return ret; @@ -966,7 +1018,6 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - s32 ret; struct snd_soc_component *component = dai->component; struct tas2783_prv *tas_dev = snd_soc_component_get_drvdata(component); @@ -975,14 +1026,11 @@ static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, sdw_stream_remove_slave(tas_dev->sdw_peripheral, sdw_stream); - mutex_lock(&tas_dev->pde_lock); - ret = regmap_write(tas_dev->regmap, - SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, - TAS2783_SDCA_CTL_REQ_POW_STATE, 0), - TAS2783_SDCA_POW_STATE_OFF); - mutex_unlock(&tas_dev->pde_lock); - - return ret; + guard(mutex)(&tas_dev->pde_lock); + return regmap_write(tas_dev->regmap, + SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, + TAS2783_SDCA_CTL_REQ_POW_STATE, 0), + TAS2783_SDCA_POW_STATE_OFF); } static const struct snd_soc_dai_ops tas_dai_ops = { @@ -1099,7 +1147,13 @@ static s32 tas2783_sdca_dev_resume(struct device *dev) } regcache_cache_only(tas_dev->regmap, false); - regcache_sync(tas_dev->regmap); + ret = regcache_sync(tas_dev->regmap); + if (ret) { + regcache_cache_only(tas_dev->regmap, true); + regcache_mark_dirty(tas_dev->regmap); + return ret; + } + return 0; } @@ -1225,9 +1279,23 @@ static s32 tas_update_status(struct sdw_slave *slave, if (tas_dev->hw_init || tas_dev->status != SDW_SLAVE_ATTACHED) return 0; - /* updated the cache data to device */ regcache_cache_only(tas_dev->regmap, false); - regcache_sync(tas_dev->regmap); + + /* + * The device is attaching uninitialized: either this is the first + * attach, or it lost power (and with it all register and DSP state) + * while the controller was power-gated during system suspend. The + * cache still holds the pre-suspend values, and tas_io_init() below + * resets the device via TAS2783_SW_RESET anyway, so syncing it back + * is both useless and harmful: later read-modify-write updates would + * compare against stale data and skip the hardware write. + * + * Drop the cache instead, so that subsequent accesses see the real + * hardware state. Syncing after the reset is not an option either: + * the cache accepts registers for which tas2783_sdca_mbq_size() + * returns 0, and writing those back fails with -EINVAL. + */ + regcache_drop_region(tas_dev->regmap, 0, UINT_MAX); /* perform I/O transfers required for Slave initialization */ return tas_io_init(&slave->dev, slave); @@ -1330,7 +1398,7 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, function_data->desc = &peripheral->sdca_data.function[i]; /* Parse the function */ - ret = sdca_parse_function(dev, peripheral, function_data); + ret = sdca_parse_function(dev, function_data); if (!ret) tas_dev->sa_func_data = function_data; else diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c index bcc8cab8d667..d32796a6fa75 100644 --- a/sound/soc/codecs/tas5805m.c +++ b/sound/soc/codecs/tas5805m.c @@ -12,6 +12,7 @@ // // It has been simplified a little and reworked for the 5.x ALSA SoC API. +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -230,10 +231,9 @@ static int tas5805m_vol_get(struct snd_kcontrol *kcontrol, struct tas5805m_priv *tas5805m = snd_soc_component_get_drvdata(component); - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); ucontrol->value.integer.value[0] = tas5805m->vol[0]; ucontrol->value.integer.value[1] = tas5805m->vol[1]; - mutex_unlock(&tas5805m->lock); return 0; } @@ -249,13 +249,12 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_component_get_drvdata(component); - int ret = 0; if (!(volume_is_valid(ucontrol->value.integer.value[0]) && volume_is_valid(ucontrol->value.integer.value[1]))) return -EINVAL; - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); if (tas5805m->vol[0] != ucontrol->value.integer.value[0] || tas5805m->vol[1] != ucontrol->value.integer.value[1]) { tas5805m->vol[0] = ucontrol->value.integer.value[0]; @@ -265,11 +264,10 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, tas5805m->is_powered); if (tas5805m->is_powered) tas5805m_refresh(tas5805m); - ret = 1; + return 1; } - mutex_unlock(&tas5805m->lock); - return ret; + return 0; } static const struct snd_kcontrol_new tas5805m_snd_controls[] = { @@ -332,7 +330,7 @@ static void do_work(struct work_struct *work) dev_dbg(&tas5805m->i2c->dev, "DSP startup\n"); - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); /* We mustn't issue any I2C transactions until the I2S * clock is stable. Furthermore, we must allow a 5ms * delay after the first set of register writes to @@ -345,7 +343,6 @@ static void do_work(struct work_struct *work) tas5805m->is_powered = true; tas5805m_refresh(tas5805m); - mutex_unlock(&tas5805m->lock); } static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, @@ -362,7 +359,7 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, dev_dbg(component->dev, "DSP shutdown\n"); cancel_work_sync(&tas5805m->work); - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); if (tas5805m->is_powered) { tas5805m->is_powered = false; @@ -379,7 +376,6 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ); } - mutex_unlock(&tas5805m->lock); } return 0; @@ -414,14 +410,13 @@ static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction) struct tas5805m_priv *tas5805m = snd_soc_component_get_drvdata(component); - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n", mute, tas5805m->is_powered); tas5805m->is_muted = mute; if (tas5805m->is_powered) tas5805m_refresh(tas5805m); - mutex_unlock(&tas5805m->lock); return 0; } @@ -462,7 +457,6 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) struct tas5805m_priv *tas5805m; char filename[128]; const char *config_name; - const struct firmware *fw; int ret; regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap); @@ -507,24 +501,20 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) snprintf(filename, sizeof(filename), "tas5805m_dsp_%s.bin", config_name); + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, filename, dev); if (ret) return ret; if ((fw->size < 2) || (fw->size & 1)) { dev_err(dev, "firmware is invalid\n"); - release_firmware(fw); return -EINVAL; } tas5805m->dsp_cfg_len = fw->size; tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL); - if (!tas5805m->dsp_cfg_data) { - release_firmware(fw); + if (!tas5805m->dsp_cfg_data) return -ENOMEM; - } - - release_firmware(fw); /* Do the first part of the power-on here, while we can expect * the I2S interface to be quiet. We must raise PDN# and then diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c index 82526362de7b..404706b62156 100644 --- a/sound/soc/codecs/tas675x.c +++ b/sound/soc/codecs/tas675x.c @@ -1924,15 +1924,6 @@ static const struct reg_default tas675x_reg_defaults[] = { { TAS675X_PWM_PHASE_M_CTRL_CH2_REG, 0x00 }, { TAS675X_PWM_PHASE_M_CTRL_CH3_REG, 0x00 }, { TAS675X_PWM_PHASE_M_CTRL_CH4_REG, 0x00 }, - { TAS675X_DC_LDG_CTRL_REG, 0x00 }, - { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 }, - { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 }, - { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 }, - { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 }, - { TAS675X_AC_LDG_CTRL_REG, 0x10 }, - { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 }, - { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 }, - { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 }, { TAS675X_REPORT_ROUTING_1_REG, 0x00 }, { TAS675X_OTSD_RECOVERY_EN_REG, 0x00 }, { TAS675X_REPORT_ROUTING_2_REG, 0xA2 }, @@ -1943,6 +1934,15 @@ static const struct reg_default tas675x_reg_defaults[] = { { TAS675X_GPIO1_OUTPUT_SEL_REG, 0x00 }, { TAS675X_GPIO2_OUTPUT_SEL_REG, 0x00 }, { TAS675X_GPIO_CTRL_REG, TAS675X_GPIO_CTRL_RSTVAL }, + { TAS675X_DC_LDG_CTRL_REG, 0x00 }, + { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 }, + { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 }, + { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 }, + { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 }, + { TAS675X_AC_LDG_CTRL_REG, 0x10 }, + { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 }, + { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 }, + { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 }, { TAS675X_OTW_CTRL_CH1_CH2_REG, 0x11 }, { TAS675X_OTW_CTRL_CH3_CH4_REG, 0x11 }, }; diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index e5dfb3d752a3..84e954311c1b 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -320,8 +320,14 @@ static int aic26_probe(struct snd_soc_component *component) return 0; } +static void aic26_remove(struct snd_soc_component *component) +{ + device_remove_file(component->dev, &dev_attr_keyclick); +} + static const struct snd_soc_component_driver aic26_soc_component_dev = { .probe = aic26_probe, + .remove = aic26_remove, .controls = aic26_snd_controls, .num_controls = ARRAY_SIZE(aic26_snd_controls), .dapm_widgets = tlv320aic26_dapm_widgets, diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 1d2e0ea6d4fe..43bcbc5449e1 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1720,18 +1720,14 @@ static int tlv320dac3100_fw_load(struct aic31xx_priv *aic31xx, static int tlv320dac3100_load_coeffs(struct aic31xx_priv *aic31xx, const char *fw_name) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int ret; ret = request_firmware(&fw, fw_name, aic31xx->dev); if (ret) return ret; - ret = tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); - - release_firmware(fw); - - return ret; + return tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); } static int aic31xx_i2c_probe(struct i2c_client *i2c) diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index 5c0a76a4a106..deed61650e09 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0 - * +// SPDX-License-Identifier: GPL-2.0 +/* * Clock Tree for the Texas Instruments TLV320AIC32x4 * * Copyright 2019 Annaliese McDermond @@ -9,6 +9,7 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> +#include <linux/delay.h> #include <linux/regmap.h> #include <linux/device.h> @@ -57,7 +58,7 @@ static void clk_aic32x4_pll_unprepare(struct clk_hw *hw) struct clk_aic32x4 *pll = to_clk_aic32x4(hw); regmap_update_bits(pll->regmap, AIC32X4_PLLPR, - AIC32X4_PLLEN, 0); + AIC32X4_PLLEN, 0); } static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) @@ -75,7 +76,7 @@ static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) } static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, - struct clk_aic32x4_pll_muldiv *settings) + struct clk_aic32x4_pll_muldiv *settings) { /* Change to use regmap_bulk_read? */ unsigned int val; @@ -106,19 +107,19 @@ static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, } static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, - struct clk_aic32x4_pll_muldiv *settings) + struct clk_aic32x4_pll_muldiv *settings) { int ret; /* Change to use regmap_bulk_write for some if not all? */ ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, - AIC32X4_PLL_R_MASK, settings->r); + AIC32X4_PLL_R_MASK, settings->r); if (ret < 0) return ret; ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, - AIC32X4_PLL_P_MASK, - settings->p << AIC32X4_PLL_P_SHIFT); + AIC32X4_PLL_P_MASK, + settings->p << AIC32X4_PLL_P_SHIFT); if (ret < 0) return ret; @@ -136,23 +137,21 @@ static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, return 0; } -static unsigned long clk_aic32x4_pll_calc_rate( - struct clk_aic32x4_pll_muldiv *settings, - unsigned long parent_rate) +static unsigned long clk_aic32x4_pll_calc_rate(struct clk_aic32x4_pll_muldiv *settings, + unsigned long parent_rate) { u64 rate; /* * We scale j by 10000 to account for the decimal part of P and divide * it back out later. */ - rate = (u64) parent_rate * settings->r * - ((settings->j * 10000) + settings->d); + rate = (u64)parent_rate * settings->r * ((settings->j * 10000) + settings->d); - return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000); + return (unsigned long)DIV_ROUND_UP_ULL(rate, settings->p * 10000); } static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, - unsigned long rate, unsigned long parent_rate) + unsigned long rate, unsigned long parent_rate) { u64 multiplier; @@ -165,14 +164,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, * of the multiplier. This is because we can't do floating point * math in the kernel. */ - multiplier = (u64) rate * settings->p * 10000; + multiplier = (u64)rate * settings->p * 10000; do_div(multiplier, parent_rate); /* * J can't be over 64, so R can scale this. * R can't be greater than 4. */ - settings->r = ((u32) multiplier / 640000) + 1; + settings->r = ((u32)multiplier / 640000) + 1; if (settings->r > 4) return -1; do_div(multiplier, settings->r); @@ -184,14 +183,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, return -1; /* Figure out the integer part, J, and the fractional part, D. */ - settings->j = (u32) multiplier / 10000; - settings->d = (u32) multiplier % 10000; + settings->j = (u32)multiplier / 10000; + settings->d = (u32)multiplier % 10000; return 0; } static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) + unsigned long parent_rate) { struct clk_aic32x4 *pll = to_clk_aic32x4(hw); struct clk_aic32x4_pll_muldiv settings; @@ -220,8 +219,8 @@ static int clk_aic32x4_pll_determine_rate(struct clk_hw *hw, } static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) + unsigned long rate, + unsigned long parent_rate) { struct clk_aic32x4 *pll = to_clk_aic32x4(hw); struct clk_aic32x4_pll_muldiv settings; @@ -236,7 +235,7 @@ static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, return ret; /* 10ms is the delay to wait before the clocks are stable */ - msleep(10); + usleep_range(10000, 20000); return 0; } @@ -261,7 +260,6 @@ static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw) return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT; } - static const struct clk_ops aic32x4_pll_ops = { .prepare = clk_aic32x4_pll_prepare, .unprepare = clk_aic32x4_pll_unprepare, @@ -311,11 +309,11 @@ static void clk_aic32x4_div_unprepare(struct clk_hw *hw) struct clk_aic32x4 *div = to_clk_aic32x4(hw); regmap_update_bits(div->regmap, div->reg, - AIC32X4_DIVEN, 0); + AIC32X4_DIVEN, 0); } static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) + unsigned long parent_rate) { struct clk_aic32x4 *div = to_clk_aic32x4(hw); u8 divisor; @@ -342,7 +340,7 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, } static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) + unsigned long parent_rate) { struct clk_aic32x4 *div = to_clk_aic32x4(hw); unsigned int val; @@ -399,7 +397,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { { .name = "pll", .parent_names = - (const char* []) { "mclk", "bclk", "gpio", "din" }, + (const char *[]) { "mclk", "bclk", "gpio", "din" }, .num_parents = 4, .ops = &aic32x4_pll_ops, .reg = 0, @@ -414,28 +412,28 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { }, { .name = "ndac", - .parent_names = (const char * []) { "codec_clkin" }, + .parent_names = (const char *[]) { "codec_clkin" }, .num_parents = 1, .ops = &aic32x4_div_ops, .reg = AIC32X4_NDAC, }, { .name = "mdac", - .parent_names = (const char * []) { "ndac" }, + .parent_names = (const char *[]) { "ndac" }, .num_parents = 1, .ops = &aic32x4_div_ops, .reg = AIC32X4_MDAC, }, { .name = "nadc", - .parent_names = (const char * []) { "codec_clkin" }, + .parent_names = (const char *[]) { "codec_clkin" }, .num_parents = 1, .ops = &aic32x4_div_ops, .reg = AIC32X4_NADC, }, { .name = "madc", - .parent_names = (const char * []) { "nadc" }, + .parent_names = (const char *[]) { "nadc" }, .num_parents = 1, .ops = &aic32x4_div_ops, .reg = AIC32X4_MADC, @@ -451,7 +449,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { }; static struct clk *aic32x4_register_clk(struct device *dev, - struct aic32x4_clkdesc *desc) + struct aic32x4_clkdesc *desc) { struct clk_init_data init; struct clk_aic32x4 *priv; @@ -464,8 +462,8 @@ static struct clk *aic32x4_register_clk(struct device *dev, init.flags = 0; priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL); - if (priv == NULL) - return (struct clk *) -ENOMEM; + if (!priv) + return ERR_PTR(-ENOMEM); priv->dev = dev; priv->hw.init = &init; diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c index 449353d5f088..cbbb8ff3ee38 100644 --- a/sound/soc/codecs/tlv320aic32x4-i2c.c +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -1,11 +1,10 @@ -/* SPDX-License-Identifier: GPL-2.0 - * +// SPDX-License-Identifier: GPL-2.0 +/* * Copyright 2011-2019 NW Digital Radio * * Author: Annaliese McDermond <nh6z@nh6z.net> * * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. - * */ #include <linux/i2c.h> @@ -16,17 +15,23 @@ #include "tlv320aic32x4.h" +static const struct regmap_config aic32x4_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AIC32X4_REFPOWERUP, + .ranges = aic32x4_regmap_pages, + .num_ranges = 1, +}; + static int aic32x4_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; - struct regmap_config config; enum aic32x4_type type; - config = aic32x4_regmap_config; - config.reg_bits = 8; - config.val_bits = 8; + regmap = devm_regmap_init_i2c(i2c, &aic32x4_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - regmap = devm_regmap_init_i2c(i2c, &config); type = (uintptr_t)i2c_get_match_data(i2c); return aic32x4_probe(&i2c->dev, regmap, type); diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c index 92246243ff94..cb615b2e4f02 100644 --- a/sound/soc/codecs/tlv320aic32x4-spi.c +++ b/sound/soc/codecs/tlv320aic32x4-spi.c @@ -1,11 +1,10 @@ -/* SPDX-License-Identifier: GPL-2.0 - * +// SPDX-License-Identifier: GPL-2.0 +/* * Copyright 2011-2019 NW Digital Radio * * Author: Annaliese McDermond <nh6z@nh6z.net> * * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. - * */ #include <linux/spi/spi.h> @@ -16,19 +15,25 @@ #include "tlv320aic32x4.h" +static const struct regmap_config aic32x4_spi_regmap_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .read_flag_mask = 0x01, + .max_register = AIC32X4_REFPOWERUP, + .ranges = aic32x4_regmap_pages, + .num_ranges = 1, +}; + static int aic32x4_spi_probe(struct spi_device *spi) { struct regmap *regmap; - struct regmap_config config; enum aic32x4_type type; - config = aic32x4_regmap_config; - config.reg_bits = 7; - config.pad_bits = 1; - config.val_bits = 8; - config.read_flag_mask = 0x01; + regmap = devm_regmap_init_spi(spi, &aic32x4_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - regmap = devm_regmap_init_spi(spi, &config); type = (uintptr_t)spi_get_device_match_data(spi); return aic32x4_probe(&spi->dev, regmap, type); diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index d85094557215..9e0e4aa2b09c 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * linux/sound/soc/codecs/tlv320aic32x4.c - * * Copyright 2011 Vista Silicon S.L. * * Author: Javier Martin <javier.martin@vista-silicon.com> @@ -28,7 +26,6 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/tlv.h> -#include <sound/tlv320aic32x4.h> #include "tlv320aic32x4.h" @@ -45,7 +42,8 @@ struct aic32x4_priv { struct regulator *supply_dv; struct regulator *supply_av; - struct aic32x4_setup_data *setup; + unsigned int gpio_func[5]; + struct device *dev; enum aic32x4_type type; @@ -75,7 +73,7 @@ static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w, }; static int mic_bias_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); @@ -83,25 +81,23 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: /* Change Mic Bias Registor */ snd_soc_component_update_bits(component, AIC32X4_MICBIAS, - AIC32x4_MICBIAS_MASK, - AIC32X4_MICBIAS_LDOIN | - AIC32X4_MICBIAS_2075V); - printk(KERN_DEBUG "%s: Mic Bias will be turned ON\n", __func__); + AIC32x4_MICBIAS_MASK, + AIC32X4_MICBIAS_LDOIN | + AIC32X4_MICBIAS_2075V); + dev_dbg(component->dev, "Mic Bias will be turned ON\n"); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_component_update_bits(component, AIC32X4_MICBIAS, - AIC32x4_MICBIAS_MASK, 0); - printk(KERN_DEBUG "%s: Mic Bias will be turned OFF\n", - __func__); + AIC32x4_MICBIAS_MASK, 0); + dev_dbg(component->dev, "Mic Bias will be turned OFF\n"); break; } return 0; } - static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -114,7 +110,7 @@ static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -123,8 +119,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, val = snd_soc_component_read(component, AIC32X4_DOUTCTL); gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { - printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n", - __func__); + dev_err(component->dev, "MFP2 is not configure as a GPIO output\n"); return -EINVAL; } @@ -142,7 +137,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -155,7 +150,7 @@ static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -164,8 +159,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, val = snd_soc_component_read(component, AIC32X4_MISOCTL); gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { - printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n", - __func__); + dev_err(component->dev, "MFP4 is not configure as a GPIO output\n"); return -EINVAL; } @@ -183,7 +177,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -195,7 +189,7 @@ static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -204,8 +198,7 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, val = snd_soc_component_read(component, AIC32X4_GPIOCTL); gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT); if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) { - printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n", - __func__); + dev_err(component->dev, "MFP5 is not configure as a GPIO output\n"); return -EINVAL; } @@ -222,25 +215,31 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, return 0; }; -static const struct snd_kcontrol_new aic32x4_mfp1[] = { - SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), -}; - -static const struct snd_kcontrol_new aic32x4_mfp2[] = { - SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), -}; - -static const struct snd_kcontrol_new aic32x4_mfp3[] = { - SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), -}; - -static const struct snd_kcontrol_new aic32x4_mfp4[] = { - SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), -}; - -static const struct snd_kcontrol_new aic32x4_mfp5[] = { - SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, - aic32x4_set_mfp5_gpio), +static const struct { + unsigned int reg; + struct snd_kcontrol_new ctrl; +} aic32x4_mfp_cfg[] = { + { + .reg = AIC32X4_DINCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), + }, + { + .reg = AIC32X4_DOUTCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), + }, + { + .reg = AIC32X4_SCLKCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), + }, + { + .reg = AIC32X4_MISOCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), + }, + { + .reg = AIC32X4_GPIOCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, + aic32x4_set_mfp5_gpio), + }, }; /* 0dB min, 0.5dB steps */ @@ -270,53 +269,42 @@ static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text); static const struct snd_kcontrol_new aic32x4_snd_controls[] = { SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL, - AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), + AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum), SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum), SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, - AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, - tlv_driver_gain), + AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, - AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, - tlv_driver_gain), + AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, - AIC32X4_HPRGAIN, 6, 0x01, 1), + AIC32X4_HPRGAIN, 6, 0x01, 1), SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, - AIC32X4_LORGAIN, 6, 0x01, 1), + AIC32X4_LORGAIN, 6, 0x01, 1), SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum), SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, - AIC32X4_RMICPGAVOL, 7, 0x01, 1), + AIC32X4_RMICPGAVOL, 7, 0x01, 1), SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL, - AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), + AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, - AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), - SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, - 4, 0x07, 0), - SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, - 0, 0x03, 0), - SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, - 6, 0x03, 0), - SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, - 1, 0x1F, 0), - SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, - 0, 0x7F, 0), - SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, - 3, 0x1F, 0), - SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, - 3, 0x1F, 0), - SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, - 0, 0x1F, 0), - SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, - 0, 0x0F, 0), + SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, 4, 0x07, 0), + SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, 0, 0x03, 0), + SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, 6, 0x03, 0), + SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, 1, 0x1F, 0), + SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, 0, 0x7F, 0), + SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, 0, 0x1F, 0), + SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, 0, 0x0F, 0), }; static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { @@ -354,21 +342,27 @@ static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_te static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum), }; + static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum), }; + static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum), }; + static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum), }; + static const struct snd_kcontrol_new cml_to_lmixer_controls[] = { SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum), }; + static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum), }; + static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum), }; @@ -385,21 +379,27 @@ static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_te static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum), }; + static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum), }; + static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum), }; + static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum), }; + static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = { SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum), }; + static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum), }; + static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum), }; @@ -428,38 +428,38 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in1r_to_rmixer_controls), + in1r_to_rmixer_controls), SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in2r_to_rmixer_controls), + in2r_to_rmixer_controls), SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in3r_to_rmixer_controls), + in3r_to_rmixer_controls), SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in2l_to_rmixer_controls), + in2l_to_rmixer_controls), SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - cmr_to_rmixer_controls), + cmr_to_rmixer_controls), SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - in1l_to_rmixer_controls), + in1l_to_rmixer_controls), SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - in3l_to_rmixer_controls), + in3l_to_rmixer_controls), SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in1l_to_lmixer_controls), + in1l_to_lmixer_controls), SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in2l_to_lmixer_controls), + in2l_to_lmixer_controls), SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in3l_to_lmixer_controls), + in3l_to_lmixer_controls), SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in1r_to_lmixer_controls), + in1r_to_lmixer_controls), SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - cml_to_lmixer_controls), + cml_to_lmixer_controls), SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - in2r_to_lmixer_controls), + in2r_to_lmixer_controls), SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - in3r_to_lmixer_controls), + in3r_to_lmixer_controls), SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_POST("ADC Reset", aic32x4_reset_adc), @@ -575,7 +575,7 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"}, }; -static const struct regmap_range_cfg aic32x4_regmap_pages[] = { +const struct regmap_range_cfg aic32x4_regmap_pages[] = { { .selector_reg = 0, .selector_mask = 0xff, @@ -585,13 +585,7 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = { .range_max = AIC32X4_REFPOWERUP, }, }; - -const struct regmap_config aic32x4_regmap_config = { - .max_register = AIC32X4_REFPOWERUP, - .ranges = aic32x4_regmap_pages, - .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages), -}; -EXPORT_SYMBOL(aic32x4_regmap_config); +EXPORT_SYMBOL_GPL(aic32x4_regmap_pages); static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) @@ -624,7 +618,7 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) case SND_SOC_DAIFMT_CBC_CFC: break; default: - printk(KERN_ERR "aic32x4: invalid clock provider\n"); + dev_err(component->dev, "invalid clock provider\n"); return -EINVAL; } @@ -651,19 +645,20 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) AIC32X4_IFACE1_DATATYPE_SHIFT); break; default: - printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); + dev_err(component->dev, "invalid DAI interface format\n"); return -EINVAL; } aic32x4->fmt = fmt; snd_soc_component_update_bits(component, AIC32X4_IFACE1, - AIC32X4_IFACE1_DATATYPE_MASK | - AIC32X4_IFACE1_MASTER_MASK, iface_reg_1); + AIC32X4_IFACE1_DATATYPE_MASK | + AIC32X4_IFACE1_MASTER_MASK, + iface_reg_1); snd_soc_component_update_bits(component, AIC32X4_IFACE2, - AIC32X4_DATA_OFFSET_MASK, iface_reg_2); + AIC32X4_DATA_OFFSET_MASK, iface_reg_2); snd_soc_component_update_bits(component, AIC32X4_IFACE3, - AIC32X4_BCLKINV_MASK, iface_reg_3); + AIC32X4_BCLKINV_MASK, iface_reg_3); return 0; } @@ -676,14 +671,13 @@ static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr) static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) { snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8); - snd_soc_component_write(component, AIC32X4_DOSRLSB, - (dosr & 0xff)); + snd_soc_component_write(component, AIC32X4_DOSRLSB, dosr & 0xff); return 0; } static int aic32x4_set_processing_blocks(struct snd_soc_component *component, - u8 r_block, u8 p_block) + u8 r_block, u8 p_block) { struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); @@ -703,6 +697,45 @@ static int aic32x4_set_processing_blocks(struct snd_soc_component *component, return 0; } +static int aic32x4_configure_rate(struct snd_soc_component *component, + unsigned int rate, u8 *aosr, u8 *adc_rc, + u8 *dac_rc, u8 *dosr_inc) +{ + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + u8 prb_rx, prb_tx; + + if (rate <= 48000) { + *aosr = 128; + *adc_rc = 6; + *dac_rc = 8; + *dosr_inc = 8; + prb_rx = 1; + prb_tx = 1; + } else if (rate <= 96000) { + *aosr = 64; + *adc_rc = 6; + *dac_rc = 8; + *dosr_inc = 4; + prb_rx = 1; + prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 9; + } else if (rate == 192000) { + *aosr = 32; + *adc_rc = 3; + *dac_rc = 4; + *dosr_inc = 2; + prb_rx = 13; + prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 19; + } else { + dev_err(component->dev, "Sampling rate %u not supported\n", rate); + return -EINVAL; + } + + if (aic32x4->type == AIC32X4_TYPE_TAS2505) + prb_rx = 0; + + return aic32x4_set_processing_blocks(component, prb_rx, prb_tx); +} + static int aic32x4_setup_clocks(struct snd_soc_component *component, unsigned int sample_rate, unsigned int channels, unsigned int bit_depth) @@ -717,7 +750,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, unsigned long adc_clock_rate, dac_clock_rate; int ret; - static struct clk_bulk_data clocks[] = { + struct clk_bulk_data clocks[] = { { .id = "pll" }, { .id = "nadc" }, { .id = "madc" }, @@ -729,37 +762,11 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, if (ret) return ret; - if (sample_rate <= 48000) { - aosr = 128; - adc_resource_class = 6; - dac_resource_class = 8; - dosr_increment = 8; - if (aic32x4->type == AIC32X4_TYPE_TAS2505) - aic32x4_set_processing_blocks(component, 0, 1); - else - aic32x4_set_processing_blocks(component, 1, 1); - } else if (sample_rate <= 96000) { - aosr = 64; - adc_resource_class = 6; - dac_resource_class = 8; - dosr_increment = 4; - if (aic32x4->type == AIC32X4_TYPE_TAS2505) - aic32x4_set_processing_blocks(component, 0, 1); - else - aic32x4_set_processing_blocks(component, 1, 9); - } else if (sample_rate == 192000) { - aosr = 32; - adc_resource_class = 3; - dac_resource_class = 4; - dosr_increment = 2; - if (aic32x4->type == AIC32X4_TYPE_TAS2505) - aic32x4_set_processing_blocks(component, 0, 1); - else - aic32x4_set_processing_blocks(component, 13, 19); - } else { - dev_err(component->dev, "Sampling rate not supported\n"); - return -EINVAL; - } + ret = aic32x4_configure_rate(component, sample_rate, &aosr, + &adc_resource_class, &dac_resource_class, + &dosr_increment); + if (ret) + return ret; /* PCM over I2S is always 2-channel */ if ((aic32x4->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) @@ -781,50 +788,39 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, (min_mdac * dosr * sample_rate); for (mdac = min_mdac; mdac <= 128; ++mdac) { for (ndac = max_ndac; ndac > 0; --ndac) { - dac_clock_rate = ndac * mdac * dosr * - sample_rate; - if (dac_clock_rate == adc_clock_rate) { - if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) - continue; - - clk_set_rate(clocks[0].clk, - dac_clock_rate); - - clk_set_rate(clocks[1].clk, - sample_rate * aosr * - madc); - clk_set_rate(clocks[2].clk, - sample_rate * aosr); - aic32x4_set_aosr(component, - aosr); - - clk_set_rate(clocks[3].clk, - sample_rate * dosr * - mdac); - clk_set_rate(clocks[4].clk, - sample_rate * dosr); - aic32x4_set_dosr(component, - dosr); - - clk_set_rate(clocks[5].clk, - sample_rate * channels * - bit_depth); - - return 0; - } + dac_clock_rate = ndac * mdac * dosr * sample_rate; + if (dac_clock_rate != adc_clock_rate) + continue; + + if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) + continue; + + clk_set_rate(clocks[0].clk, dac_clock_rate); + + clk_set_rate(clocks[1].clk, sample_rate * aosr * madc); + clk_set_rate(clocks[2].clk, sample_rate * aosr); + aic32x4_set_aosr(component, aosr); + + clk_set_rate(clocks[3].clk, sample_rate * dosr * mdac); + clk_set_rate(clocks[4].clk, sample_rate * dosr); + aic32x4_set_dosr(component, dosr); + + clk_set_rate(clocks[5].clk, + sample_rate * channels * bit_depth); + + return 0; } } } } - dev_err(component->dev, - "Could not set clocks to support sample rate.\n"); + dev_err(component->dev, "Could not set clocks to support sample rate.\n"); return -EINVAL; } static int aic32x4_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); @@ -837,24 +833,20 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, switch (params_physical_width(params)) { case 16: - iface1_reg |= (AIC32X4_WORD_LEN_16BITS << - AIC32X4_IFACE1_DATALEN_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_16BITS << AIC32X4_IFACE1_DATALEN_SHIFT); break; case 20: - iface1_reg |= (AIC32X4_WORD_LEN_20BITS << - AIC32X4_IFACE1_DATALEN_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_IFACE1_DATALEN_SHIFT); break; case 24: - iface1_reg |= (AIC32X4_WORD_LEN_24BITS << - AIC32X4_IFACE1_DATALEN_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_IFACE1_DATALEN_SHIFT); break; case 32: - iface1_reg |= (AIC32X4_WORD_LEN_32BITS << - AIC32X4_IFACE1_DATALEN_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_IFACE1_DATALEN_SHIFT); break; } snd_soc_component_update_bits(component, AIC32X4_IFACE1, - AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); + AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); if (params_channels(params) == 1) { dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; @@ -865,7 +857,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; } snd_soc_component_update_bits(component, AIC32X4_DACSETUP, - AIC32X4_DAC_CHAN_MASK, dacsetup_reg); + AIC32X4_DAC_CHAN_MASK, dacsetup_reg); return 0; } @@ -875,7 +867,7 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute, int direction) struct snd_soc_component *component = dai->component; snd_soc_component_update_bits(component, AIC32X4_DACMUTE, - AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); + AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); return 0; } @@ -886,7 +878,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); int ret; - static struct clk_bulk_data clocks[] = { + struct clk_bulk_data clocks[] = { { .id = "madc" }, { .id = "mdac" }, { .id = "bdiv" }, @@ -955,44 +947,14 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component) struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); /* setup GPIO functions */ - /* MFP1 */ - if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_DINCTL, - aic32x4->setup->gpio_func[0]); - snd_soc_add_component_controls(component, aic32x4_mfp1, - ARRAY_SIZE(aic32x4_mfp1)); - } - - /* MFP2 */ - if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_DOUTCTL, - aic32x4->setup->gpio_func[1]); - snd_soc_add_component_controls(component, aic32x4_mfp2, - ARRAY_SIZE(aic32x4_mfp2)); - } - - /* MFP3 */ - if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_SCLKCTL, - aic32x4->setup->gpio_func[2]); - snd_soc_add_component_controls(component, aic32x4_mfp3, - ARRAY_SIZE(aic32x4_mfp3)); - } - - /* MFP4 */ - if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_MISOCTL, - aic32x4->setup->gpio_func[3]); - snd_soc_add_component_controls(component, aic32x4_mfp4, - ARRAY_SIZE(aic32x4_mfp4)); - } - - /* MFP5 */ - if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_GPIOCTL, - aic32x4->setup->gpio_func[4]); - snd_soc_add_component_controls(component, aic32x4_mfp5, - ARRAY_SIZE(aic32x4_mfp5)); + BUILD_BUG_ON(ARRAY_SIZE(aic32x4->gpio_func) != ARRAY_SIZE(aic32x4_mfp_cfg)); + for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) { + if (aic32x4->gpio_func[i] == AIC32X4_MFPX_DEFAULT_VALUE) + continue; + + snd_soc_component_write(component, aic32x4_mfp_cfg[i].reg, + aic32x4->gpio_func[i]); + snd_soc_add_component_controls(component, &aic32x4_mfp_cfg[i].ctrl, 1); } } @@ -1002,7 +964,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) u32 tmp_reg; int ret; - static struct clk_bulk_data clocks[] = { + struct clk_bulk_data clocks[] = { { .id = "codec_clkin" }, { .id = "pll" }, { .id = "bdiv" }, @@ -1013,8 +975,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) if (ret) return ret; - if (aic32x4->setup) - aic32x4_setup_gpios(component); + aic32x4_setup_gpios(component); clk_set_parent(clocks[0].clk, clocks[1].clk); clk_set_parent(clocks[2].clk, clocks[3].clk); @@ -1022,7 +983,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) /* Power platform configuration */ if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { snd_soc_component_write(component, AIC32X4_MICBIAS, - AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); + AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); } if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); @@ -1041,16 +1002,16 @@ static int aic32x4_component_probe(struct snd_soc_component *component) /* Mic PGA routing */ if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) snd_soc_component_write(component, AIC32X4_LMICPGANIN, - AIC32X4_LMICPGANIN_IN2R_10K); + AIC32X4_LMICPGANIN_IN2R_10K); else snd_soc_component_write(component, AIC32X4_LMICPGANIN, - AIC32X4_LMICPGANIN_CM1L_10K); + AIC32X4_LMICPGANIN_CM1L_10K); if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) snd_soc_component_write(component, AIC32X4_RMICPGANIN, - AIC32X4_RMICPGANIN_IN1L_10K); + AIC32X4_RMICPGANIN_IN1L_10K); else snd_soc_component_write(component, AIC32X4_RMICPGANIN, - AIC32X4_RMICPGANIN_CM1R_10K); + AIC32X4_RMICPGANIN_CM1R_10K); /* * Workaround: for an unknown reason, the ADC needs to be powered up @@ -1058,8 +1019,8 @@ static int aic32x4_component_probe(struct snd_soc_component *component) * a HW BUG or some kind of behavior not documented in the datasheet. */ tmp_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP); - snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg | - AIC32X4_LADC_EN | AIC32X4_RADC_EN); + snd_soc_component_write(component, AIC32X4_ADCSETUP, + tmp_reg | AIC32X4_LADC_EN | AIC32X4_RADC_EN); snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg); /* @@ -1102,13 +1063,13 @@ static const struct snd_kcontrol_new aic32x4_tas2505_snd_controls[] = { SOC_ENUM("DAC Playback PowerTune Switch", l_ptm_enum), SOC_SINGLE_TLV("HP Driver Gain Volume", - AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), + AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), SOC_SINGLE("HP DAC Playback Switch", AIC32X4_HPLGAIN, 6, 1, 1), SOC_SINGLE_TLV("Speaker Driver Playback Volume", - TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), + TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), SOC_SINGLE_TLV("Speaker Amplifier Playback Volume", - TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), + TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), }; @@ -1144,11 +1105,12 @@ static const struct snd_soc_dapm_route aic32x4_tas2505_dapm_routes[] = { static struct snd_soc_dai_driver aic32x4_tas2505_dai = { .name = "tas2505-hifi", .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = AIC32X4_FORMATS,}, + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = AIC32X4_FORMATS, + }, .ops = &aic32x4_ops, .symmetric_rate = 1, }; @@ -1159,7 +1121,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) u32 tmp_reg; int ret; - static struct clk_bulk_data clocks[] = { + struct clk_bulk_data clocks[] = { { .id = "codec_clkin" }, { .id = "pll" }, { .id = "bdiv" }, @@ -1170,8 +1132,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) if (ret) return ret; - if (aic32x4->setup) - aic32x4_setup_gpios(component); + aic32x4_setup_gpios(component); clk_set_parent(clocks[0].clk, clocks[1].clk); clk_set_parent(clocks[2].clk, clocks[3].clk); @@ -1192,7 +1153,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg); /* - * Enable the fast charging feature and ensure the needed 40ms ellapsed + * Enable the fast charging feature and ensure the needed 40ms elapsed * before using the analog circuits. */ snd_soc_component_write(component, TAS2505_REFPOWERUP, @@ -1218,17 +1179,10 @@ static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = .endianness = 1, }; -static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, - struct device_node *np) +static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, struct device_node *np) { - struct aic32x4_setup_data *aic32x4_setup; int ret; - aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup), - GFP_KERNEL); - if (!aic32x4_setup) - return -ENOMEM; - ret = of_property_match_string(np, "clock-names", "mclk"); if (ret < 0) return -EINVAL; @@ -1245,9 +1199,11 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn"); } - if (of_property_read_u32_array(np, "aic32x4-gpio-func", - aic32x4_setup->gpio_func, 5) >= 0) - aic32x4->setup = aic32x4_setup; + for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) + aic32x4->gpio_func[i] = AIC32X4_MFPX_DEFAULT_VALUE; + of_property_read_u32_array(np, "aic32x4-gpio-func", + aic32x4->gpio_func, ARRAY_SIZE(aic32x4->gpio_func)); + return 0; } @@ -1266,7 +1222,7 @@ static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4) } static int aic32x4_setup_regulators(struct device *dev, - struct aic32x4_priv *aic32x4) + struct aic32x4_priv *aic32x4) { int ret = 0; @@ -1356,12 +1312,8 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, struct device_node *np = dev->of_node; int ret; - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), - GFP_KERNEL); - if (aic32x4 == NULL) + aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), GFP_KERNEL); + if (!aic32x4) return -ENOMEM; aic32x4->dev = dev; @@ -1405,11 +1357,13 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, switch (aic32x4->type) { case AIC32X4_TYPE_TAS2505: ret = devm_snd_soc_register_component(dev, - &soc_component_dev_aic32x4_tas2505, &aic32x4_tas2505_dai, 1); + &soc_component_dev_aic32x4_tas2505, + &aic32x4_tas2505_dai, 1); break; default: ret = devm_snd_soc_register_component(dev, - &soc_component_dev_aic32x4, &aic32x4_dai, 1); + &soc_component_dev_aic32x4, + &aic32x4_dai, 1); } if (ret) { diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index f68a846ef61d..cfab6f8ce5ac 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -3,7 +3,6 @@ * tlv320aic32x4.h */ - #ifndef _TLV320AIC32X4_H #define _TLV320AIC32X4_H @@ -16,7 +15,7 @@ enum aic32x4_type { AIC32X4_TYPE_TAS2505, }; -extern const struct regmap_config aic32x4_regmap_config; +extern const struct regmap_range_cfg aic32x4_regmap_pages[]; int aic32x4_probe(struct device *dev, struct regmap *regmap, enum aic32x4_type type); void aic32x4_remove(struct device *dev); @@ -24,7 +23,7 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); /* tlv320aic32x4 register space (in decimal to match datasheet) */ -#define AIC32X4_REG(page, reg) ((page * 128) + reg) +#define AIC32X4_REG(page, reg) (((page) * 128) + (reg)) #define AIC32X4_PSEL AIC32X4_REG(0, 0) @@ -234,4 +233,31 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); #define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000 #define AIC32X4_MAX_PLL_CLKIN 20000000 +#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 +#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 +#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 +#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 +#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 + +#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 +#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 + +/* GPIO API */ +#define AIC32X4_MFPX_DEFAULT_VALUE 0xff + +#define AIC32X4_MFP1_DIN_DISABLED 0 +#define AIC32X4_MFP1_DIN_ENABLED 0x2 +#define AIC32X4_MFP1_GPIO_IN 0x4 + +#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 +#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 + +#define AIC32X4_MFP_GPIO_ENABLED 0x4 + +#define AIC32X4_MFP5_GPIO_DISABLED 0x0 +#define AIC32X4_MFP5_GPIO_INPUT 0x8 +#define AIC32X4_MFP5_GPIO_OUTPUT 0xc +#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 +#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 + #endif /* _TLV320AIC32X4_H */ diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 223c49dfc450..9bd2ddd8dacd 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -7,6 +7,7 @@ * Copyright: (C) 2009 Nokia Corporation */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -236,13 +237,10 @@ static int dac33_write_locked(struct snd_soc_component *component, unsigned int unsigned int value) { struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&dac33->mutex); - ret = dac33_write(component, reg, value); - mutex_unlock(&dac33->mutex); + guard(mutex)(&dac33->mutex); - return ret; + return dac33_write(component, reg, value); } #define DAC33_I2C_ADDR_AUTOINC 0x80 @@ -365,13 +363,13 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); int ret = 0; - mutex_lock(&dac33->mutex); + guard(mutex)(&dac33->mutex); /* Safety check */ if (unlikely(power == dac33->chip_power)) { dev_dbg(component->dev, "Trying to set the same power state: %s\n", power ? "ON" : "OFF"); - goto exit; + return ret; } if (power) { @@ -380,7 +378,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret != 0) { dev_err(component->dev, "Failed to enable supplies: %d\n", ret); - goto exit; + return ret; } if (dac33->reset_gpiod) { @@ -388,7 +386,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret < 0) { dev_err(&dac33->i2c->dev, "Failed to set reset GPIO: %d\n", ret); - goto exit; + return ret; } } @@ -400,7 +398,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret < 0) { dev_err(&dac33->i2c->dev, "Failed to set reset GPIO: %d\n", ret); - goto exit; + return ret; } } @@ -409,14 +407,12 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret != 0) { dev_err(component->dev, "Failed to disable supplies: %d\n", ret); - goto exit; + return ret; } dac33->chip_power = 0; } -exit: - mutex_unlock(&dac33->mutex); return ret; } @@ -659,7 +655,6 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) { struct snd_soc_component *component = dac33->component; unsigned int delay; - unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: @@ -667,10 +662,10 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) DAC33_THRREG(dac33->nsample)); /* Take the timestamps */ - spin_lock_irqsave(&dac33->lock, flags); - dac33->t_stamp2 = ktime_to_us(ktime_get()); - dac33->t_stamp1 = dac33->t_stamp2; - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) { + dac33->t_stamp2 = ktime_to_us(ktime_get()); + dac33->t_stamp1 = dac33->t_stamp2; + } dac33_write16(component, DAC33_PREFILL_MSB, DAC33_THRREG(dac33->alarm_threshold)); @@ -682,11 +677,11 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) break; case DAC33_FIFO_MODE7: /* Take the timestamp */ - spin_lock_irqsave(&dac33->lock, flags); - dac33->t_stamp1 = ktime_to_us(ktime_get()); - /* Move back the timestamp with drain time */ - dac33->t_stamp1 -= dac33->mode7_us_to_lthr; - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) { + dac33->t_stamp1 = ktime_to_us(ktime_get()); + /* Move back the timestamp with drain time */ + dac33->t_stamp1 -= dac33->mode7_us_to_lthr; + } dac33_write16(component, DAC33_PREFILL_MSB, DAC33_THRREG(DAC33_MODE7_MARGIN)); @@ -704,14 +699,12 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) { struct snd_soc_component *component = dac33->component; - unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: /* Take the timestamp */ - spin_lock_irqsave(&dac33->lock, flags); - dac33->t_stamp2 = ktime_to_us(ktime_get()); - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) + dac33->t_stamp2 = ktime_to_us(ktime_get()); dac33_write16(component, DAC33_NSAMPLE_MSB, DAC33_THRREG(dac33->nsample)); @@ -735,7 +728,7 @@ static void dac33_work(struct work_struct *work) dac33 = container_of(work, struct tlv320dac33_priv, work); component = dac33->component; - mutex_lock(&dac33->mutex); + guard(mutex)(&dac33->mutex); switch (dac33->state) { case DAC33_PREFILL: dac33->state = DAC33_PLAYBACK; @@ -757,18 +750,15 @@ static void dac33_work(struct work_struct *work) dac33_write(component, DAC33_FIFO_CTRL_A, reg); break; } - mutex_unlock(&dac33->mutex); } static irqreturn_t dac33_interrupt_handler(int irq, void *dev) { struct snd_soc_component *component = dev; struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); - unsigned long flags; - spin_lock_irqsave(&dac33->lock, flags); - dac33->t_stamp1 = ktime_to_us(ktime_get()); - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) + dac33->t_stamp1 = ktime_to_us(ktime_get()); /* Do not schedule the workqueue in Mode7 */ if (dac33->fifo_mode != DAC33_FIFO_MODE7) @@ -902,14 +892,13 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, return -EINVAL; } - mutex_lock(&dac33->mutex); + guard(mutex)(&dac33->mutex); if (!dac33->chip_power) { /* * Chip is not powered yet. * Do the init in the dac33_set_bias_level later. */ - mutex_unlock(&dac33->mutex); return 0; } @@ -1053,8 +1042,6 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, break; } - mutex_unlock(&dac33->mutex); - return 0; } @@ -1156,21 +1143,20 @@ static snd_pcm_sframes_t dac33_dai_delay( unsigned int time_delta, uthr; int samples_out, samples_in, samples; snd_pcm_sframes_t delay = 0; - unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_BYPASS: break; case DAC33_FIFO_MODE1: - spin_lock_irqsave(&dac33->lock, flags); - t0 = dac33->t_stamp1; - t1 = dac33->t_stamp2; - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) { + t0 = dac33->t_stamp1; + t1 = dac33->t_stamp2; + } t_now = ktime_to_us(ktime_get()); /* We have not started to fill the FIFO yet, delay is 0 */ if (!t1) - goto out; + return 0; if (t0 > t1) { /* @@ -1230,23 +1216,22 @@ static snd_pcm_sframes_t dac33_dai_delay( } break; case DAC33_FIFO_MODE7: - spin_lock_irqsave(&dac33->lock, flags); - t0 = dac33->t_stamp1; - uthr = dac33->uthr; - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) { + t0 = dac33->t_stamp1; + uthr = dac33->uthr; + } t_now = ktime_to_us(ktime_get()); /* We have not started to fill the FIFO yet, delay is 0 */ if (!t0) - goto out; + return 0; if (t_now <= t0) { /* * Either the timestamps are messed or equal. Report * maximum delay */ - delay = uthr; - goto out; + return uthr; } time_delta = t_now - t0; @@ -1287,7 +1272,7 @@ static snd_pcm_sframes_t dac33_dai_delay( dac33->fifo_mode); break; } -out: + return delay; } diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index dba581857920..02082ef790b4 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/mutex.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <sound/tlv.h> #include <sound/pcm_params.h> @@ -210,25 +211,21 @@ static int power_up_audio_plls(struct snd_soc_component *component) return ret; } - mutex_lock(&tscs42xx->pll_lock); + guard(mutex)(&tscs42xx->pll_lock); ret = snd_soc_component_update_bits(component, R_PLLCTL1C, mask, val); if (ret < 0) { dev_err(component->dev, "Failed to turn PLL on (%d)\n", ret); - goto exit; + return ret; } if (!plls_locked(component)) { dev_err(component->dev, "Failed to lock plls\n"); ret = -ENOMSG; - goto exit; + return ret; } - ret = 0; -exit: - mutex_unlock(&tscs42xx->pll_lock); - - return ret; + return 0; } static int power_down_audio_plls(struct snd_soc_component *component) @@ -236,28 +233,24 @@ static int power_down_audio_plls(struct snd_soc_component *component) struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); int ret; - mutex_lock(&tscs42xx->pll_lock); + guard(mutex)(&tscs42xx->pll_lock); ret = snd_soc_component_update_bits(component, R_PLLCTL1C, RM_PLLCTL1C_PDB_PLL1, RV_PLLCTL1C_PDB_PLL1_DISABLE); if (ret < 0) { dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); - goto exit; + return ret; } ret = snd_soc_component_update_bits(component, R_PLLCTL1C, RM_PLLCTL1C_PDB_PLL2, RV_PLLCTL1C_PDB_PLL2_DISABLE); if (ret < 0) { dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); - goto exit; + return ret; } - ret = 0; -exit: - mutex_unlock(&tscs42xx->pll_lock); - - return ret; + return 0; } static int coeff_ram_get(struct snd_kcontrol *kcontrol, @@ -269,13 +262,11 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, (struct coeff_ram_ctl *)kcontrol->private_value; struct soc_bytes_ext *params = &ctl->bytes_ext; - mutex_lock(&tscs42xx->coeff_ram_lock); + guard(mutex)(&tscs42xx->coeff_ram_lock); memcpy(ucontrol->value.bytes.data, &tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max); - mutex_unlock(&tscs42xx->coeff_ram_lock); - return 0; } @@ -290,14 +281,14 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, unsigned int coeff_cnt = params->max / COEFF_SIZE; int ret; - mutex_lock(&tscs42xx->coeff_ram_lock); + guard(mutex)(&tscs42xx->coeff_ram_lock); tscs42xx->coeff_ram_synced = false; memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], ucontrol->value.bytes.data, params->max); - mutex_lock(&tscs42xx->pll_lock); + guard(mutex)(&tscs42xx->pll_lock); if (plls_locked(component)) { ret = write_coeff_ram(component, tscs42xx->coeff_ram, @@ -305,18 +296,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, if (ret < 0) { dev_err(component->dev, "Failed to flush coeff ram cache (%d)\n", ret); - goto exit; + return ret; } tscs42xx->coeff_ram_synced = true; } - ret = 0; -exit: - mutex_unlock(&tscs42xx->pll_lock); - - mutex_unlock(&tscs42xx->coeff_ram_lock); - - return ret; + return 0; } /* Input L Capture Route */ @@ -385,21 +370,17 @@ static int dac_event(struct snd_soc_dapm_widget *w, struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); int ret; - mutex_lock(&tscs42xx->coeff_ram_lock); + guard(mutex)(&tscs42xx->coeff_ram_lock); if (!tscs42xx->coeff_ram_synced) { ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00, COEFF_RAM_COEFF_COUNT); if (ret < 0) - goto exit; + return ret; tscs42xx->coeff_ram_synced = true; } - ret = 0; -exit: - mutex_unlock(&tscs42xx->coeff_ram_lock); - - return ret; + return 0; } static const struct snd_soc_dapm_widget tscs42xx_dapm_widgets[] = { @@ -926,12 +907,10 @@ static int setup_sample_rate(struct snd_soc_component *component, return ret; } - mutex_lock(&tscs42xx->audio_params_lock); + guard(mutex)(&tscs42xx->audio_params_lock); tscs42xx->samplerate = rate; - mutex_unlock(&tscs42xx->audio_params_lock); - return 0; } @@ -1253,12 +1232,10 @@ static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, return ret; } - mutex_lock(&tscs42xx->audio_params_lock); + guard(mutex)(&tscs42xx->audio_params_lock); tscs42xx->bclk_ratio = ratio; - mutex_unlock(&tscs42xx->audio_params_lock); - return 0; } diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index aad394937ce6..b70c9d931e1e 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -4,6 +4,7 @@ // Author: Steven Eckhoff <steven.eckhoff.opensource@gmail.com> #include <linux/kernel.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/regmap.h> @@ -329,12 +330,10 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, return -EINVAL; } - mutex_lock(coeff_ram_lock); - - memcpy(ucontrol->value.bytes.data, - &coeff_ram[ctl->addr * COEFF_SIZE], params->max); - - mutex_unlock(coeff_ram_lock); + scoped_guard(mutex, coeff_ram_lock) { + memcpy(ucontrol->value.bytes.data, + &coeff_ram[ctl->addr * COEFF_SIZE], params->max); + } return 0; } @@ -428,15 +427,15 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - mutex_lock(coeff_ram_lock); + guard(mutex)(coeff_ram_lock); *coeff_ram_synced = false; memcpy(&coeff_ram[ctl->addr * COEFF_SIZE], ucontrol->value.bytes.data, params->max); - mutex_lock(&tscs454->pll1.lock); - mutex_lock(&tscs454->pll2.lock); + guard(mutex)(&tscs454->pll1.lock); + guard(mutex)(&tscs454->pll2.lock); val = snd_soc_component_read(component, R_PLLSTAT); if (val) { /* PLLs locked */ @@ -446,18 +445,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, if (ret < 0) { dev_err(component->dev, "Failed to flush coeff ram cache (%d)\n", ret); - goto exit; + return ret; } *coeff_ram_synced = true; } - ret = 0; -exit: - mutex_unlock(&tscs454->pll2.lock); - mutex_unlock(&tscs454->pll1.lock); - mutex_unlock(coeff_ram_lock); - - return ret; + return 0; } static inline int coeff_ram_sync(struct snd_soc_component *component, @@ -465,41 +458,35 @@ static inline int coeff_ram_sync(struct snd_soc_component *component, { int ret; - mutex_lock(&tscs454->dac_ram.lock); - if (!tscs454->dac_ram.synced) { - ret = write_coeff_ram(component, tscs454->dac_ram.cache, - R_DACCRS, R_DACCRADD, R_DACCRWDL, - 0x00, COEFF_RAM_COEFF_COUNT); - if (ret < 0) { - mutex_unlock(&tscs454->dac_ram.lock); - return ret; + scoped_guard(mutex, &tscs454->dac_ram.lock) { + if (!tscs454->dac_ram.synced) { + ret = write_coeff_ram(component, tscs454->dac_ram.cache, + R_DACCRS, R_DACCRADD, R_DACCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) + return ret; } } - mutex_unlock(&tscs454->dac_ram.lock); - mutex_lock(&tscs454->spk_ram.lock); - if (!tscs454->spk_ram.synced) { - ret = write_coeff_ram(component, tscs454->spk_ram.cache, - R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, - 0x00, COEFF_RAM_COEFF_COUNT); - if (ret < 0) { - mutex_unlock(&tscs454->spk_ram.lock); - return ret; + scoped_guard(mutex, &tscs454->spk_ram.lock) { + if (!tscs454->spk_ram.synced) { + ret = write_coeff_ram(component, tscs454->spk_ram.cache, + R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) + return ret; } } - mutex_unlock(&tscs454->spk_ram.lock); - mutex_lock(&tscs454->sub_ram.lock); - if (!tscs454->sub_ram.synced) { - ret = write_coeff_ram(component, tscs454->sub_ram.cache, - R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, - 0x00, COEFF_RAM_COEFF_COUNT); - if (ret < 0) { - mutex_unlock(&tscs454->sub_ram.lock); - return ret; + scoped_guard(mutex, &tscs454->sub_ram.lock) { + if (!tscs454->sub_ram.synced) { + ret = write_coeff_ram(component, tscs454->sub_ram.cache, + R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) + return ret; } } - mutex_unlock(&tscs454->sub_ram.lock); return 0; } @@ -658,16 +645,14 @@ static int set_sysclk(struct snd_soc_component *component) static inline void reserve_pll(struct pll *pll) { - mutex_lock(&pll->lock); + guard(mutex)(&pll->lock); pll->users++; - mutex_unlock(&pll->lock); } static inline void free_pll(struct pll *pll) { - mutex_lock(&pll->lock); + guard(mutex)(&pll->lock); pll->users--; - mutex_unlock(&pll->lock); } static int pll_connected(struct snd_soc_dapm_widget *source, @@ -679,15 +664,13 @@ static int pll_connected(struct snd_soc_dapm_widget *source, int users; if (strstr(source->name, "PLL 1")) { - mutex_lock(&tscs454->pll1.lock); - users = tscs454->pll1.users; - mutex_unlock(&tscs454->pll1.lock); + scoped_guard(mutex, &tscs454->pll1.lock) + users = tscs454->pll1.users; dev_dbg(component->dev, "%s(): PLL 1 users = %d\n", __func__, users); } else { - mutex_lock(&tscs454->pll2.lock); - users = tscs454->pll2.users; - mutex_unlock(&tscs454->pll2.lock); + scoped_guard(mutex, &tscs454->pll2.lock) + users = tscs454->pll2.users; dev_dbg(component->dev, "%s(): PLL 2 users = %d\n", __func__, users); } @@ -806,7 +789,7 @@ static inline int aif_free(struct snd_soc_component *component, { struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); - mutex_lock(&tscs454->aifs_status_lock); + guard(mutex)(&tscs454->aifs_status_lock); dev_dbg(component->dev, "%s(): aif %d\n", __func__, aif->id); @@ -829,8 +812,6 @@ static inline int aif_free(struct snd_soc_component *component, free_pll(tscs454->internal_rate.pll); } - mutex_unlock(&tscs454->aifs_status_lock); - return 0; } @@ -3174,7 +3155,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, unsigned int val; int ret; - mutex_lock(&tscs454->aifs_status_lock); + guard(mutex)(&tscs454->aifs_status_lock); dev_dbg(component->dev, "%s(): aif %d fs = %u\n", __func__, aif->id, fs); @@ -3207,14 +3188,14 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, ret = set_aif_fs(component, aif->id, fs); if (ret < 0) { dev_err(component->dev, "Failed to set aif fs (%d)\n", ret); - goto exit; + return ret; } ret = set_aif_sample_format(component, params_format(params), aif->id); if (ret < 0) { dev_err(component->dev, "Failed to set aif sample format (%d)\n", ret); - goto exit; + return ret; } set_aif_status_active(&tscs454->aifs_status, aif->id, @@ -3223,11 +3204,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, dev_dbg(component->dev, "Set aif %d active. Streams status is 0x%x\n", aif->id, tscs454->aifs_status.streams); - ret = 0; -exit: - mutex_unlock(&tscs454->aifs_status_lock); - - return ret; + return 0; } static int tscs454_hw_free(struct snd_pcm_substream *substream, diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index e10c51092a35..650836f9615f 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/platform_device.h> @@ -273,7 +274,7 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, struct twl6040_data *priv = snd_soc_component_get_drvdata(component); int status; - mutex_lock(&priv->mutex); + guard(mutex)(&priv->mutex); /* Sync status */ status = twl6040_read(component, TWL6040_REG_STATUS); @@ -281,8 +282,6 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, snd_soc_jack_report(jack, report, report); else snd_soc_jack_report(jack, 0, report); - - mutex_unlock(&priv->mutex); } void twl6040_hs_jack_detect(struct snd_soc_component *component, diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c index 12f5757f4210..4e2d6240a869 100644 --- a/sound/soc/codecs/uda1342.c +++ b/sound/soc/codecs/uda1342.c @@ -308,9 +308,16 @@ static int uda1342_suspend(struct device *dev) static int uda1342_resume(struct device *dev) { struct uda1342_priv *uda1342 = dev_get_drvdata(dev); + int ret; + regcache_cache_only(uda1342->regmap, false); regcache_mark_dirty(uda1342->regmap); - regcache_sync(uda1342->regmap); + ret = regcache_sync(uda1342->regmap); + if (ret) { + regcache_cache_only(uda1342->regmap, true); + regcache_mark_dirty(uda1342->regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index e3ca5ca6de3d..40de279a9750 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -5010,9 +5010,9 @@ static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) */ usleep_range(600, 650); - gpiod_set_value(wcd->reset_gpio, 1); + gpiod_set_value_cansleep(wcd->reset_gpio, 1); msleep(20); - gpiod_set_value(wcd->reset_gpio, 0); + gpiod_set_value_cansleep(wcd->reset_gpio, 0); msleep(20); return 0; diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index bc41a1466c70..caca5cc25100 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -1265,13 +1265,10 @@ static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) { - mutex_lock(&wcd->sysclk_mutex); - - if (++wcd->sysclk_users != 1) { - mutex_unlock(&wcd->sysclk_mutex); - return 0; + scoped_guard(mutex, &wcd->sysclk_mutex) { + if (++wcd->sysclk_users != 1) + return 0; } - mutex_unlock(&wcd->sysclk_mutex); regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, WCD934X_ANA_BIAS_EN_MASK, @@ -1328,12 +1325,10 @@ static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd) { - mutex_lock(&wcd->sysclk_mutex); - if (--wcd->sysclk_users != 0) { - mutex_unlock(&wcd->sysclk_mutex); - return 0; + scoped_guard(mutex, &wcd->sysclk_mutex) { + if (--wcd->sysclk_users != 0) + return 0; } - mutex_unlock(&wcd->sysclk_mutex); regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, WCD934X_EXT_CLK_BUF_EN_MASK | @@ -2384,7 +2379,7 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, __func__, micb_num); return -EINVAL; } - mutex_lock(&wcd934x->micb_lock); + guard(mutex)(&wcd934x->micb_lock); switch (req) { case MICB_PULLUP_ENABLE: @@ -2446,8 +2441,6 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, break; } - mutex_unlock(&wcd934x->micb_lock); - return 0; } @@ -2488,7 +2481,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, int req_volt, int micb_num) { struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component); - int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; switch (micb_num) { case MIC_BIAS_1: @@ -2506,7 +2499,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, default: return -EINVAL; } - mutex_lock(&wcd934x->micb_lock); + guard(mutex)(&wcd934x->micb_lock); /* * If requested micbias voltage is same as current micbias * voltage, then just return. Otherwise, adjust voltage as @@ -2521,15 +2514,11 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, WCD934X_MICB_VAL_MASK); req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); - if (req_vout_ctl < 0) { - ret = -EINVAL; - goto exit; - } + if (req_vout_ctl < 0) + return -EINVAL; - if (cur_vout_ctl == req_vout_ctl) { - ret = 0; - goto exit; - } + if (cur_vout_ctl == req_vout_ctl) + return 0; if (micb_en == WCD934X_MICB_ENABLE) snd_soc_component_write_field(component, micb_reg, @@ -2550,9 +2539,8 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, */ usleep_range(2000, 2100); } -exit: - mutex_unlock(&wcd934x->micb_lock); - return ret; + + return 0; } static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, @@ -5899,10 +5887,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev) } static const struct platform_device_id wcd934x_driver_id[] = { - { - .name = "wcd934x-codec", - }, - {}, + { .name = "wcd934x-codec" }, + { } }; MODULE_DEVICE_TABLE(platform, wcd934x_driver_id); diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c index 7a18bed7f347..1ad75c226060 100644 --- a/sound/soc/codecs/wcd937x-sdw.c +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -1084,10 +1084,16 @@ static int wcd937x_sdw_runtime_suspend(struct device *dev) static int wcd937x_sdw_runtime_resume(struct device *dev) { struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); + int ret; if (wcd->regmap) { regcache_cache_only(wcd->regmap, false); - regcache_sync(wcd->regmap); + ret = regcache_sync(wcd->regmap); + if (ret) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + return ret; + } } return 0; diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index e0169e783ee9..0dd05604f5b8 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -2,6 +2,7 @@ // Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. #include <linux/component.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/gpio/consumer.h> @@ -1056,7 +1057,7 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, return -EINVAL; } - mutex_lock(&wcd937x->micb_lock); + guard(mutex)(&wcd937x->micb_lock); switch (req) { case MICB_PULLUP_ENABLE: wcd937x->pullup_ref[micb_index]++; @@ -1136,7 +1137,6 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, } break; } - mutex_unlock(&wcd937x->micb_lock); return 0; } @@ -1460,7 +1460,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, int req_volt, int micb_num) { struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); - int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; switch (micb_num) { case MIC_BIAS_1: @@ -1475,7 +1475,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, default: return -EINVAL; } - mutex_lock(&wcd937x->micb_lock); + guard(mutex)(&wcd937x->micb_lock); /* * If requested micbias voltage is same as current micbias * voltage, then just return. Otherwise, adjust voltage as @@ -1490,15 +1490,11 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, WCD937X_MICB_VOUT_MASK); req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); - if (req_vout_ctl < 0) { - ret = -EINVAL; - goto exit; - } + if (req_vout_ctl < 0) + return -EINVAL; - if (cur_vout_ctl == req_vout_ctl) { - ret = 0; - goto exit; - } + if (cur_vout_ctl == req_vout_ctl) + return 0; if (micb_en == WCD937X_MICB_ENABLE) snd_soc_component_write_field(component, micb_reg, @@ -1519,9 +1515,8 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, */ usleep_range(2000, 2100); } -exit: - mutex_unlock(&wcd937x->micb_lock); - return ret; + + return 0; } static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 0f0cc0ac3056..e5ad368418a2 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1245,10 +1245,16 @@ static int wcd938x_sdw_runtime_suspend(struct device *dev) static int wcd938x_sdw_runtime_resume(struct device *dev) { struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); + int ret; if (wcd->regmap) { regcache_cache_only(wcd->regmap, false); - regcache_sync(wcd->regmap); + ret = regcache_sync(wcd->regmap); + if (ret) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + return ret; + } } pm_runtime_mark_last_busy(dev); diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index c69e18667a85..9a9ea37ecab3 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/platform_device.h> @@ -1976,7 +1977,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, int req_volt, int micb_num) { struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; switch (micb_num) { case MIC_BIAS_1: @@ -1994,7 +1995,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, default: return -EINVAL; } - mutex_lock(&wcd938x->micb_lock); + guard(mutex)(&wcd938x->micb_lock); /* * If requested micbias voltage is same as current micbias * voltage, then just return. Otherwise, adjust voltage as @@ -2009,15 +2010,11 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, WCD938X_MICB_VOUT_MASK); req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); - if (req_vout_ctl < 0) { - ret = -EINVAL; - goto exit; - } + if (req_vout_ctl < 0) + return -EINVAL; - if (cur_vout_ctl == req_vout_ctl) { - ret = 0; - goto exit; - } + if (cur_vout_ctl == req_vout_ctl) + return 0; if (micb_en == WCD938X_MICB_ENABLE) snd_soc_component_write_field(component, micb_reg, @@ -2038,9 +2035,8 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, */ usleep_range(2000, 2100); } -exit: - mutex_unlock(&wcd938x->micb_lock); - return ret; + + return 0; } static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c index 95f4be287e79..69b9b08118f0 100644 --- a/sound/soc/codecs/wcd939x-sdw.c +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -1431,10 +1431,16 @@ static int wcd939x_sdw_runtime_suspend(struct device *dev) static int wcd939x_sdw_runtime_resume(struct device *dev) { struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + int ret; if (wcd->regmap) { regcache_cache_only(wcd->regmap, false); - regcache_sync(wcd->regmap); + ret = regcache_sync(wcd->regmap); + if (ret) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + return ret; + } } return 0; diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 010d12466722..26fe0b6313cd 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/platform_device.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> @@ -1923,7 +1924,6 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); unsigned int micb_reg, cur_vout_ctl, micb_en; int req_vout_ctl; - int ret = 0; switch (micb_num) { case MIC_BIAS_1: @@ -1941,7 +1941,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, default: return -EINVAL; } - mutex_lock(&wcd939x->micb_lock); + guard(mutex)(&wcd939x->micb_lock); /* * If requested micbias voltage is same as current micbias @@ -1957,15 +1957,11 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, WCD939X_MICB_VOUT_CTL); req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); - if (req_vout_ctl < 0) { - ret = req_vout_ctl; - goto exit; - } + if (req_vout_ctl < 0) + return req_vout_ctl; - if (cur_vout_ctl == req_vout_ctl) { - ret = 0; - goto exit; - } + if (cur_vout_ctl == req_vout_ctl) + return 0; dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), @@ -1990,9 +1986,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, usleep_range(2000, 2100); } -exit: - mutex_unlock(&wcd939x->micb_lock); - return ret; + return 0; } static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 2a8c61a72c17..c44abffe9b56 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -9,6 +9,7 @@ * Scott Ling <sl@opensource.wolfsonmicro.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/interrupt.h> @@ -148,13 +149,11 @@ static const char *wm0010_state_to_str(enum wm0010_state state) static void wm0010_halt(struct snd_soc_component *component) { struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); - unsigned long flags; enum wm0010_state state; /* Fetch the wm0010 state */ - spin_lock_irqsave(&wm0010->irq_lock, flags); - state = wm0010->state; - spin_unlock_irqrestore(&wm0010->irq_lock, flags); + scoped_guard(spinlock_irqsave, &wm0010->irq_lock) + state = wm0010->state; switch (state) { case WM0010_POWER_OFF: @@ -173,9 +172,8 @@ static void wm0010_halt(struct snd_soc_component *component) break; } - spin_lock_irqsave(&wm0010->irq_lock, flags); - wm0010->state = WM0010_POWER_OFF; - spin_unlock_irqrestore(&wm0010->irq_lock, flags); + scoped_guard(spinlock_irqsave, &wm0010->irq_lock) + wm0010->state = WM0010_POWER_OFF; } struct wm0010_boot_xfer { @@ -190,11 +188,9 @@ struct wm0010_boot_xfer { static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010) { enum wm0010_state state; - unsigned long flags; - spin_lock_irqsave(&wm0010->irq_lock, flags); - state = wm0010->state; - spin_unlock_irqrestore(&wm0010->irq_lock, flags); + scoped_guard(spinlock_irqsave, &wm0010->irq_lock) + state = wm0010->state; dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n", wm0010_state_to_str(state), wm0010_state_to_str(state + 1)); @@ -337,7 +333,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp struct wm0010_boot_xfer *xfer; int ret; DECLARE_COMPLETION_ONSTACK(done); - const struct firmware *fw; const struct dfw_binrec *rec; const struct dfw_inforec *inforec; u64 *img; @@ -346,6 +341,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp INIT_LIST_HEAD(&xfer_list); + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, name, component->dev); if (ret != 0) { dev_err(component->dev, "Failed to request application(%s): %d\n", @@ -364,16 +360,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp /* First record should be INFO */ if (rec->command != DFW_CMD_INFO) { dev_err(component->dev, "First record not INFO\r\n"); - ret = -EINVAL; - goto abort; + return -EINVAL; } if (inforec->info_version != INFO_VERSION) { dev_err(component->dev, "Unsupported version (%02d) of INFO record\r\n", inforec->info_version); - ret = -EINVAL; - goto abort; + return -EINVAL; } dev_dbg(component->dev, "Version v%02d INFO record found\r\n", @@ -382,8 +376,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp /* Check it's a DSP file */ if (dsp != DEVICE_ID_WM0010) { dev_err(component->dev, "Not a WM0010 firmware file.\r\n"); - ret = -EINVAL; - goto abort; + return -EINVAL; } /* Skip the info record as we don't need to send it */ @@ -408,14 +401,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp out = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!out) { ret = -ENOMEM; - goto abort1; + goto abort; } xfer->t.rx_buf = out; img = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!img) { ret = -ENOMEM; - goto abort1; + goto abort; } xfer->t.tx_buf = img; @@ -453,13 +446,13 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp ret = spi_async(spi, &xfer->m); if (ret != 0) { dev_err(component->dev, "Write failed: %d\n", ret); - goto abort1; + goto abort; } if (wm0010->boot_failed) { dev_dbg(component->dev, "Boot fail!\n"); ret = -EINVAL; - goto abort1; + goto abort; } } @@ -467,7 +460,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp ret = 0; -abort1: +abort: while (!list_empty(&xfer_list)) { xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, list); @@ -477,8 +470,6 @@ abort1: kfree(xfer); } -abort: - release_firmware(fw); return ret; } @@ -486,14 +477,12 @@ static int wm0010_stage2_load(struct snd_soc_component *component) { struct spi_device *spi = to_spi_device(component->dev); struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); - const struct firmware *fw; struct spi_message m; struct spi_transfer t; - u32 *img; - u8 *out; int i; int ret = 0; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, "wm0010_stage2.bin", component->dev); if (ret != 0) { dev_err(component->dev, "Failed to request stage2 loader: %d\n", @@ -504,17 +493,15 @@ static int wm0010_stage2_load(struct snd_soc_component *component) dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size); /* Copy to local buffer first as vmalloc causes problems for dma */ - img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); - if (!img) { - ret = -ENOMEM; - goto abort2; - } + u32 *img __free(kfree) = + kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); + if (!img) + return -ENOMEM; - out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); - if (!out) { - ret = -ENOMEM; - goto abort1; - } + u8 *out __free(kfree) = + kzalloc(fw->size, GFP_KERNEL | GFP_DMA); + if (!out) + return -ENOMEM; spi_message_init(&m); memset(&t, 0, sizeof(t)); @@ -531,7 +518,7 @@ static int wm0010_stage2_load(struct snd_soc_component *component) ret = spi_sync(spi, &m); if (ret != 0) { dev_err(component->dev, "Initial download failed: %d\n", ret); - goto abort; + return ret; } /* Look for errors from the boot ROM */ @@ -540,18 +527,11 @@ static int wm0010_stage2_load(struct snd_soc_component *component) dev_err(component->dev, "Boot ROM error: %x in %d\n", out[i], i); wm0010_mark_boot_failure(wm0010); - ret = -EBUSY; - goto abort; + return -EBUSY; } } -abort: - kfree(out); -abort1: - kfree(img); -abort2: - release_firmware(fw); - return ret; + return 0; } static int wm0010_boot(struct snd_soc_component *component) @@ -734,9 +714,8 @@ static int wm0010_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_STANDBY: if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE) { - mutex_lock(&wm0010->lock); - wm0010_halt(component); - mutex_unlock(&wm0010->lock); + scoped_guard(mutex, &wm0010->lock) + wm0010_halt(component); } break; case SND_SOC_BIAS_OFF: @@ -832,9 +811,8 @@ static irqreturn_t wm0010_irq(int irq, void *data) case WM0010_OUT_OF_RESET: case WM0010_BOOTROM: case WM0010_STAGE2: - spin_lock(&wm0010->irq_lock); - complete(&wm0010->boot_completion); - spin_unlock(&wm0010->irq_lock); + scoped_guard(spinlock, &wm0010->irq_lock) + complete(&wm0010->boot_completion); return IRQ_HANDLED; default: return IRQ_NONE; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 9b68ee69324b..41c8cfb346c6 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/firmware.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/pm.h> @@ -612,20 +613,15 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); unsigned int anc_active = ucontrol->value.integer.value[0]; - int ret; if (anc_active > 1) return -EINVAL; - mutex_lock(&wm2000->lock); + guard(mutex)(&wm2000->lock); wm2000->anc_active = anc_active; - ret = wm2000_anc_set_mode(wm2000); - - mutex_unlock(&wm2000->lock); - - return ret; + return wm2000_anc_set_mode(wm2000); } static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, @@ -645,20 +641,15 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); unsigned int val = ucontrol->value.integer.value[0]; - int ret; if (val > 1) return -EINVAL; - mutex_lock(&wm2000->lock); + guard(mutex)(&wm2000->lock); wm2000->spk_ena = val; - ret = wm2000_anc_set_mode(wm2000); - - mutex_unlock(&wm2000->lock); - - return ret; + return wm2000_anc_set_mode(wm2000); } static const struct snd_kcontrol_new wm2000_controls[] = { @@ -676,9 +667,8 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); - int ret; - mutex_lock(&wm2000->lock); + guard(mutex)(&wm2000->lock); if (SND_SOC_DAPM_EVENT_ON(event)) wm2000->anc_eng_ena = 1; @@ -686,11 +676,7 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_OFF(event)) wm2000->anc_eng_ena = 0; - ret = wm2000_anc_set_mode(wm2000); - - mutex_unlock(&wm2000->lock); - - return ret; + return wm2000_anc_set_mode(wm2000); } static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { @@ -810,7 +796,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) struct wm2000_priv *wm2000; struct wm2000_platform_data *pdata; const char *filename; - const struct firmware *fw = NULL; + const struct firmware *fw __free(firmware) = NULL; int ret, i; unsigned int reg; u16 id; @@ -828,7 +814,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) ret = PTR_ERR(wm2000->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - goto out; + return ret; } for (i = 0; i < WM2000_NUM_SUPPLIES; i++) @@ -922,9 +908,6 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) err_supplies: regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); - -out: - release_firmware(fw); return ret; } diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index ba8ce2e6e615..bc78b553b6cb 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2461,7 +2461,15 @@ static int wm2200_runtime_resume(struct device *dev) } regcache_cache_only(wm2200->regmap, false); - regcache_sync(wm2200->regmap); + ret = regcache_sync(wm2200->regmap); + if (ret) { + regcache_cache_only(wm2200->regmap, true); + regcache_mark_dirty(wm2200->regmap); + gpiod_set_value_cansleep(wm2200->ldo_ena, 0); + regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), + wm2200->core_supplies); + return ret; + } return 0; } diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index bd94fa53c362..2fe937a93026 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2659,7 +2659,15 @@ static int wm5100_runtime_resume(struct device *dev) } regcache_cache_only(wm5100->regmap, false); - regcache_sync(wm5100->regmap); + ret = regcache_sync(wm5100->regmap); + if (ret) { + regcache_cache_only(wm5100->regmap, true); + regcache_mark_dirty(wm5100->regmap); + gpiod_set_value_cansleep(wm5100->ldo_ena, 0); + regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), + wm5100->core_supplies); + return ret; + } return 0; } diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index b4d4137c05b4..74b775b95bfd 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -7,6 +7,7 @@ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -667,10 +668,9 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct arizona *arizona = dev_get_drvdata(component->dev->parent); - mutex_lock(&arizona->dac_comp_lock); + guard(mutex)(&arizona->dac_comp_lock); put_unaligned_be16(arizona->dac_comp_coeff, ucontrol->value.bytes.data); - mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -681,16 +681,14 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct arizona *arizona = dev_get_drvdata(component->dev->parent); uint16_t dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data); - int ret = 0; - mutex_lock(&arizona->dac_comp_lock); + guard(mutex)(&arizona->dac_comp_lock); if (arizona->dac_comp_coeff != dac_comp_coeff) { arizona->dac_comp_coeff = dac_comp_coeff; - ret = 1; + return 1; } - mutex_unlock(&arizona->dac_comp_lock); - return ret; + return 0; } static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, @@ -699,9 +697,8 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct arizona *arizona = dev_get_drvdata(component->dev->parent); - mutex_lock(&arizona->dac_comp_lock); + guard(mutex)(&arizona->dac_comp_lock); ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; - mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -712,19 +709,17 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct arizona *arizona = dev_get_drvdata(component->dev->parent); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - int ret = 0; if (ucontrol->value.integer.value[0] > mc->max) return -EINVAL; - mutex_lock(&arizona->dac_comp_lock); + guard(mutex)(&arizona->dac_comp_lock); if (arizona->dac_comp_enabled != ucontrol->value.integer.value[0]) { arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; - ret = 1; + return 1; } - mutex_unlock(&arizona->dac_comp_lock); - return ret; + return 0; } static const char * const wm5102_osr_text[] = { diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index a2f0e2f5c407..ce87280d590d 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -10,6 +10,7 @@ * Based on wm8753.c by Liam Girdwood */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -110,22 +111,20 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); unsigned int deemph = ucontrol->value.integer.value[0]; - int ret = 0; if (deemph > 1) return -EINVAL; - mutex_lock(&wm8731->lock); + guard(mutex)(&wm8731->lock); if (wm8731->deemph != deemph) { wm8731->deemph = deemph; wm8731_set_deemph(component); - ret = 1; + return 1; } - mutex_unlock(&wm8731->lock); - return ret; + return 0; } static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 320d7737699d..156e1e24a388 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/cleanup.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/gpio/driver.h> @@ -458,22 +459,20 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); unsigned int deemph = ucontrol->value.integer.value[0]; - int ret = 0; if (deemph > 1) return -EINVAL; - mutex_lock(&wm8903->lock); + guard(mutex)(&wm8903->lock); if (wm8903->deemph != deemph) { wm8903->deemph = deemph; wm8903_set_deemph(component); - ret = 1; + return 1; } - mutex_unlock(&wm8903->lock); - return ret; + return 0; } /* ALSA can only do steps of .01dB */ diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index e631ec072249..229252363188 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -333,7 +333,7 @@ static const struct snd_soc_dapm_route wm8940_dapm_routes[] = { {"ADC", NULL, "Boost Mixer"}, }; -#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0); +#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0) static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index 8ff0882732e7..f75a6dc9d2bb 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -7,6 +7,7 @@ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -864,9 +865,8 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) { - mutex_lock(&wm8994->fw_lock); + guard(mutex)(&wm8994->fw_lock); wm8994->enh_eq = fw; - mutex_unlock(&wm8994->fw_lock); } } @@ -876,9 +876,8 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) { - mutex_lock(&wm8994->fw_lock); + guard(mutex)(&wm8994->fw_lock); wm8994->mbc_vss = fw; - mutex_unlock(&wm8994->fw_lock); } } @@ -888,9 +887,8 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) { - mutex_lock(&wm8994->fw_lock); + guard(mutex)(&wm8994->fw_lock); wm8994->mbc = fw; - mutex_unlock(&wm8994->fw_lock); } } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index de18b1f85a32..8a9598161b35 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/pm.h> @@ -1564,11 +1565,10 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); int old = wm8962->dsp2_ena; - int ret = 0; int dsp2_running = snd_soc_component_read(component, WM8962_DSP2_POWER_MANAGEMENT) & WM8962_DSP2_ENA; - mutex_lock(&wm8962->dsp2_ena_lock); + guard(mutex)(&wm8962->dsp2_ena_lock); if (ucontrol->value.integer.value[0]) wm8962->dsp2_ena |= 1 << shift; @@ -1576,9 +1576,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, wm8962->dsp2_ena &= ~(1 << shift); if (wm8962->dsp2_ena == old) - goto out; - - ret = 1; + return 0; if (dsp2_running) { if (wm8962->dsp2_ena) @@ -1587,10 +1585,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, wm8962_dsp2_stop(component); } -out: - mutex_unlock(&wm8962->dsp2_ena_lock); - - return ret; + return 1; } /* The VU bits for the headphones are in a different register to the mute @@ -3937,7 +3932,9 @@ static int wm8962_runtime_resume(struct device *dev) WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, 0); - regcache_sync(wm8962->regmap); + ret = regcache_sync(wm8962->regmap); + if (ret) + goto cache_sync_err; regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA, @@ -3955,6 +3952,11 @@ static int wm8962_runtime_resume(struct device *dev) return 0; +cache_sync_err: + regcache_cache_only(wm8962->regmap, true); + regcache_mark_dirty(wm8962->regmap); + regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), + wm8962->supplies); disable_clock: clk_disable_unprepare(wm8962->pdata.mclk); return ret; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1d64c7c42ed1..8bf58a6a8af7 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -7,6 +7,7 @@ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -766,7 +767,7 @@ static void active_reference(struct snd_soc_component *component) { struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - mutex_lock(&wm8994->accdet_lock); + guard(mutex)(&wm8994->accdet_lock); wm8994->active_refcount++; @@ -775,8 +776,6 @@ static void active_reference(struct snd_soc_component *component) /* If we're using jack detection go into audio mode */ wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_AUDIO); - - mutex_unlock(&wm8994->accdet_lock); } static void active_dereference(struct snd_soc_component *component) @@ -784,7 +783,7 @@ static void active_dereference(struct snd_soc_component *component) struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); u16 mode; - mutex_lock(&wm8994->accdet_lock); + guard(mutex)(&wm8994->accdet_lock); wm8994->active_refcount--; @@ -800,8 +799,6 @@ static void active_dereference(struct snd_soc_component *component) wm1811_jackdet_set_mode(component, mode); } - - mutex_unlock(&wm8994->accdet_lock); } static int clk_sys_event(struct snd_soc_dapm_widget *w, @@ -3704,7 +3701,7 @@ static void wm8958_open_circuit_work(struct work_struct *work) open_circuit_work.work); struct device *dev = wm8994->wm8994->dev; - mutex_lock(&wm8994->accdet_lock); + guard(mutex)(&wm8994->accdet_lock); wm1811_micd_stop(wm8994->hubs.component); @@ -3718,8 +3715,6 @@ static void wm8958_open_circuit_work(struct work_struct *work) snd_soc_jack_report(wm8994->micdet[0].jack, 0, wm8994->btn_mask | SND_JACK_HEADSET); - - mutex_unlock(&wm8994->accdet_lock); } static void wm8958_mic_id(void *data, u16 status) @@ -3777,7 +3772,7 @@ static void wm1811_mic_work(struct work_struct *work) struct snd_soc_component *component = wm8994->hubs.component; struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); - pm_runtime_get_sync(component->dev); + guard(pm_runtime_active)(component->dev); /* If required for an external cap force MICBIAS on */ if (control->pdata.jd_ext_cap) { @@ -3785,7 +3780,7 @@ static void wm1811_mic_work(struct work_struct *work) snd_soc_dapm_sync(dapm); } - mutex_lock(&wm8994->accdet_lock); + guard(mutex)(&wm8994->accdet_lock); dev_dbg(component->dev, "Starting mic detection\n"); @@ -3803,10 +3798,6 @@ static void wm1811_mic_work(struct work_struct *work) snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, WM8958_MICD_ENA); } - - mutex_unlock(&wm8994->accdet_lock); - - pm_runtime_put(component->dev); } static irqreturn_t wm1811_jackdet_irq(int irq, void *data) @@ -4026,15 +4017,10 @@ static void wm8958_mic_work(struct work_struct *work) mic_complete_work.work); struct snd_soc_component *component = wm8994->hubs.component; - pm_runtime_get_sync(component->dev); - - mutex_lock(&wm8994->accdet_lock); + guard(pm_runtime_active)(component->dev); + guard(mutex)(&wm8994->accdet_lock); wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); - - mutex_unlock(&wm8994->accdet_lock); - - pm_runtime_put(component->dev); } static irqreturn_t wm8958_mic_irq(int irq, void *data) diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 83cd42fa0c28..3105b2e0556f 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -6,6 +6,7 @@ * Author: Liam Girdwood <lrg@slimlogic.co.uk> */ +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/mfd/wm97xx.h> @@ -229,7 +230,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, shift = mc->shift & 0xff; mask = 1 << shift; - mutex_lock(&wm9712->lock); + guard(mutex)(&wm9712->lock); old = wm9712->hp_mixer[mixer]; if (ucontrol->value.integer.value[0]) wm9712->hp_mixer[mixer] |= mask; @@ -251,8 +252,6 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, &update); } - mutex_unlock(&wm9712->lock); - return change; } diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index b3bbecf074ee..3ba7ca3c1770 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -11,6 +11,7 @@ * o Support for DAPM */ +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/mfd/wm97xx.h> @@ -238,7 +239,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, shift = mc->shift & 0xff; mask = (1 << shift); - mutex_lock(&wm9713->lock); + guard(mutex)(&wm9713->lock); old = wm9713->hp_mixer[mixer]; if (ucontrol->value.integer.value[0]) wm9713->hp_mixer[mixer] |= mask; @@ -260,8 +261,6 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, &update); } - mutex_unlock(&wm9713->lock); - return change; } diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index baa75e7ff53b..90c24c4b318e 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -348,7 +348,6 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); - int ret = 1; if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) return 0; @@ -356,16 +355,14 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) return -EINVAL; - mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); + guard(mutex)(&dsp[e->shift_l].cs_dsp.pwr_lock); if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list)) - ret = -EBUSY; + return -EBUSY; else dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; - mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock); - - return ret; + return 1; } EXPORT_SYMBOL_GPL(wm_adsp_fw_put); @@ -450,15 +447,13 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, if (val == 0) return 0; /* 0 means no event */ - mutex_lock(&cs_ctl->dsp->pwr_lock); + guard(mutex)(&cs_ctl->dsp->pwr_lock); if (cs_ctl->enabled) ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); else ret = -EPERM; - mutex_unlock(&cs_ctl->dsp->pwr_lock); - if (ret < 0) return ret; @@ -486,15 +481,13 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; int ret = 0; - mutex_lock(&cs_ctl->dsp->pwr_lock); + guard(mutex)(&cs_ctl->dsp->pwr_lock); ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) ret = -EFAULT; - mutex_unlock(&cs_ctl->dsp->pwr_lock); - return ret; } @@ -694,10 +687,9 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, struct cs_dsp_coeff_ctl *cs_ctl; int ret; - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); - mutex_unlock(&dsp->cs_dsp.pwr_lock); if (ret < 0) return ret; @@ -709,14 +701,10 @@ EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, unsigned int alg, void *buf, size_t len) { - int ret; + guard(mutex)(&dsp->cs_dsp.pwr_lock); - mutex_lock(&dsp->cs_dsp.pwr_lock); - ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), + return cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), 0, buf, len); - mutex_unlock(&dsp->cs_dsp.pwr_lock); - - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); @@ -1270,38 +1258,32 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) { struct wm_adsp_compr *compr, *tmp; struct snd_soc_pcm_runtime *rtd = stream->private_data; - int ret = 0; - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); if (wm_adsp_fw[dsp->fw].num_caps == 0) { adsp_err(dsp, "%s: Firmware does not support compressed API\n", snd_soc_rtd_to_codec(rtd, 0)->name); - ret = -ENXIO; - goto out; + return -ENXIO; } if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { adsp_err(dsp, "%s: Firmware does not support stream direction\n", snd_soc_rtd_to_codec(rtd, 0)->name); - ret = -EINVAL; - goto out; + return -EINVAL; } list_for_each_entry(tmp, &dsp->compr_list, list) { if (!strcmp(tmp->name, snd_soc_rtd_to_codec(rtd, 0)->name)) { adsp_err(dsp, "%s: Only a single stream supported per dai\n", snd_soc_rtd_to_codec(rtd, 0)->name); - ret = -EBUSY; - goto out; + return -EBUSY; } } compr = kzalloc_obj(*compr); - if (!compr) { - ret = -ENOMEM; - goto out; - } + if (!compr) + return -ENOMEM; compr->dsp = dsp; compr->stream = stream; @@ -1311,10 +1293,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) stream->runtime->private_data = compr; -out: - mutex_unlock(&dsp->cs_dsp.pwr_lock); - - return ret; + return 0; } EXPORT_SYMBOL_GPL(wm_adsp_compr_open); @@ -1324,7 +1303,7 @@ int wm_adsp_compr_free(struct snd_soc_component *component, struct wm_adsp_compr *compr = stream->runtime->private_data; struct wm_adsp *dsp = compr->dsp; - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); wm_adsp_compr_detach(compr); list_del(&compr->list); @@ -1332,8 +1311,6 @@ int wm_adsp_compr_free(struct snd_soc_component *component, kfree(compr->raw_buf); kfree(compr); - mutex_unlock(&dsp->cs_dsp.pwr_lock); - return 0; } EXPORT_SYMBOL_GPL(wm_adsp_compr_free); @@ -1741,7 +1718,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, compr_dbg(compr, "Trigger: %d\n", cmd); - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -1777,8 +1754,6 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, break; } - mutex_unlock(&dsp->cs_dsp.pwr_lock); - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); @@ -1907,21 +1882,20 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, compr_dbg(compr, "Pointer request\n"); - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); buf = compr->buf; if (dsp->fatal_error || !buf || buf->error) { snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); - ret = -EIO; - goto out; + return -EIO; } if (buf->avail < wm_adsp_compr_frag_words(compr)) { ret = wm_adsp_buffer_update_avail(buf); if (ret < 0) { compr_err(compr, "Error reading avail: %d\n", ret); - goto out; + return ret; } /* @@ -1934,14 +1908,14 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, if (buf->error) snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); - goto out; + return ret; } ret = wm_adsp_buffer_reenable_irq(buf); if (ret < 0) { compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", ret); - goto out; + return ret; } } } @@ -1950,9 +1924,6 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; tstamp->sampling_rate = compr->sample_rate; -out: - mutex_unlock(&dsp->cs_dsp.pwr_lock); - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); @@ -2063,15 +2034,13 @@ int wm_adsp_compr_copy(struct snd_soc_component *component, struct wm_adsp *dsp = compr->dsp; int ret; - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); if (stream->direction == SND_COMPRESS_CAPTURE) ret = wm_adsp_compr_read(compr, buf, count); else ret = -ENOTSUPP; - mutex_unlock(&dsp->cs_dsp.pwr_lock); - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 5174614c3e83..47ac3e4985a5 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1178,7 +1178,13 @@ static int wsa881x_runtime_resume(struct device *dev) } regcache_cache_only(regmap, false); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) { + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + gpiod_direction_output(wsa881x->sd_n, 1); + return ret; + } return 0; } diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 468d2b38a22a..442cf9f3eaac 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -4,6 +4,7 @@ */ #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/hwmon.h> @@ -1237,9 +1238,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - mutex_lock(&wsa883x->sp_lock); - wsa883x->pa_on = true; - mutex_unlock(&wsa883x->sp_lock); + scoped_guard(mutex, &wsa883x->sp_lock) + wsa883x->pa_on = true; switch (wsa883x->dev_mode) { case RECEIVER: @@ -1290,9 +1290,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, WSA883X_GLOBAL_PA_EN_MASK, 0); snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, WSA883X_PDM_EN_MASK, 0); - mutex_lock(&wsa883x->sp_lock); - wsa883x->pa_on = false; - mutex_unlock(&wsa883x->sp_lock); + scoped_guard(mutex, &wsa883x->sp_lock) + wsa883x->pa_on = false; break; } return 0; @@ -1696,9 +1695,15 @@ static int wsa883x_runtime_suspend(struct device *dev) static int wsa883x_runtime_resume(struct device *dev) { struct regmap *regmap = dev_get_regmap(dev, NULL); + int ret; regcache_cache_only(regmap, false); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) { + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index 6c6b497657d0..a367c94bfb4d 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -1701,9 +1701,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - mutex_lock(&wsa884x->sp_lock); - wsa884x->pa_on = true; - mutex_unlock(&wsa884x->sp_lock); + scoped_guard(mutex, &wsa884x->sp_lock) + wsa884x->pa_on = true; wsa884x_spkr_post_pmu(component, wsa884x); @@ -1717,9 +1716,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, 0x0); - mutex_lock(&wsa884x->sp_lock); - wsa884x->pa_on = false; - mutex_unlock(&wsa884x->sp_lock); + scoped_guard(mutex, &wsa884x->sp_lock) + wsa884x->pa_on = false; break; } @@ -2147,9 +2145,15 @@ static int wsa884x_runtime_suspend(struct device *dev) static int wsa884x_runtime_resume(struct device *dev) { struct regmap *regmap = dev_get_regmap(dev, NULL); + int ret; regcache_cache_only(regmap, false); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) { + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + return ret; + } return 0; } diff --git a/sound/soc/codecs/wsa885x.c b/sound/soc/codecs/wsa885x.c new file mode 100644 index 000000000000..1faa8541a872 --- /dev/null +++ b/sound/soc/codecs/wsa885x.c @@ -0,0 +1,1533 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* WSA885X codec driver */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +/* Control Registers - Audio Processing */ +#define WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S 0x0000 +#define WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK 0x0004 +#define WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX 0x0140 +#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID 0x0208 +#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX 0x0240 +#define WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER 0x0340 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0 0x4405 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1 0x4406 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB 0x4409 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB 0x6409 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB 0x440a +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB 0x640a +#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS 0x0a04 +#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS 0x0a40 +#define WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE 0x0b10 +#define WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX 0x0e40 + +/* Analog Top Registers - Power and Clock Control */ +#define WSA885X_ANA_TOP_PON_CKSK_CTL_0 0x800d +#define WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG 0x8024 +#define WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG 0x8025 +#define WSA885X_ANA_TOP_BG_TVP_OVRD_CTL 0x8034 + +/* Analog PLL Registers */ +#define WSA885X_ANA_PLL_DIV_CTL_0 0x8090 +#define WSA885X_ANA_PLL_DIV_CTL_1 0x8091 +#define WSA885X_ANA_TOP_PLL_VCO_CTL 0x8092 +#define WSA885X_ANA_TOP_PLL_LOOPFILT_0 0x8093 +#define WSA885X_ANA_TOP_PLL_OVRD_CTL 0x8098 +#define WSA885X_ANA_TOP_PLL_STATUS_0 0x809a +#define WSA885X_ANA_TOP_PLL_STATUS_1 0x809b + +/* Analog Boost Control Registers */ +#define WSA885X_ANA_TOP_BOOST_STB_CTRL2 0x805b +#define WSA885X_ANA_TOP_BOOST_STB_CTRL3 0x805c +#define WSA885X_ANA_TOP_BOOST_BYP_CTRL2 0x805e +#define WSA885X_ANA_TOP_BOOST_BYP_CTRL3 0x805f +#define WSA885X_ANA_TOP_BOOST_MISC 0x8063 +#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2 0x8065 +#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4 0x8067 + +/* Analog IV Sense ADC Registers */ +#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2 0x80ca +#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3 0x80cb +#define WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL 0x80cc +#define WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2 0x80d0 + +/* Analog Speaker Power Stage Registers */ +#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3 0x8108 +#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3 0x810b +#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3 0x810e +#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3 0x8111 +#define WSA885X_ANA_TOP_SPK_TOP_SPARE3 0x813c +#define WSA885X_SPK_TOP_LF_CH1_CTRL11 0x811c +#define WSA885X_SPK_TOP_LF_CH1_TUNE1 0x811d +#define WSA885X_SPK_TOP_LF_CH2_TUNE1 0x8129 +#define WSA885X_SPK_TOP_LF_CH1_CTRL9 0x811a +#define WSA885X_SPK_TOP_LF_CH2_CTRL9 0x8126 +#define WSA885X_SPK_TOP_LF_CH2_CTRL11 0x8128 +#define WSA885X_SPK_TOP_COMMON_CTRL2 0x8102 +#define WSA885X_SPK_TOP_COMMON_TUNE1 0x8103 +#define WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1 0x80ba +#define WSA885X_DIG_CTRL0_TOP_CLK_CFG 0x8418 +#define WSA885X_DIG_CTRL0_SDCA_COMMIT 0x8419 +#define WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE 0x841a +#define WSA885X_DIG_CTRL0_SYS_CLK_SEL 0x841b +#define WSA885X_DIG_CTRL0_CDC_CLK_CTL 0x841c +#define WSA885X_DIG_CTRL0_PA_FSM_CTL 0x8420 +#define WSA885X_DIG_CTRL0_POWER_FSM_CTL0 0x8423 +#define WSA885X_DIG_CTRL0_POWER_FSM_CTL1 0x8424 +#define WSA885X_DIG_CTRL0_PA0_FSM_CTL1 0x842b +#define WSA885X_DIG_CTRL0_PA1_FSM_CTL1 0x8435 +#define WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL 0x8458 +#define WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL 0x8470 +#define WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1 0x84b4 +#define WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1 0x84b7 +#define WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1 0x84A0 +#define WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1 0x84A4 + +/* Digital Control 1 Registers - I2S/TDM Interface */ +#define WSA885X_DIG_CTRL1_I2S_CTL0 0x85A0 +#define WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX 0x85A2 +#define WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX 0x85A3 +#define WSA885X_DIG_CTRL1_I2S_TDM_CTL0 0x85A7 +#define WSA885X_DIG_CTRL1_I2S_TDM_CTL1 0x85A9 +#define WSA885X_DIG_CTRL1_I2S_TDM_CH_RX 0x85AA +#define WSA885X_DIG_CTRL1_I2S_TDM_CH_TX 0x85AB +#define WSA885X_DIG_CTRL1_I2S_RESET_CTL 0x85AE + +/* CDC RX Path Registers - Audio Data Path */ +#define WSA885X_CDC_RX0_RX_PATH_CFG0 0x8601 +#define WSA885X_CDC_RX0_RX_PATH_CFG1 0x8602 +#define WSA885X_CDC_RX0_RX_PATH_CTL 0x8606 +#define WSA885X_RX0_RX_PATH_DSMDEM_CTL 0x8613 +#define WSA885X_CDC_RX1_RX_PATH_CFG0 0x8621 +#define WSA885X_CDC_RX1_RX_PATH_CFG1 0x8622 +#define WSA885X_CDC_RX1_RX_PATH_CTL 0x8626 +#define WSA885X_RX1_RX_PATH_DSMDEM_CTL 0x8633 + +/* CDC Compander Registers - Dynamic Range Control */ +#define WSA885X_CDC_COMPANDER0_CTL0 0x8640 +#define WSA885X_CDC_COMPANDER0_CTL7 0x8647 +#define WSA885X_CDC_COMPANDER1_CTL0 0x8660 +#define WSA885X_CDC_COMPANDER1_CTL7 0x8667 + +/* CDC Speaker Protection Registers - IV Sense */ +#define WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL 0x86A1 +#define WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL 0x86B1 +#define WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL 0x86A9 +#define WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL 0x86B9 + +/* CDC Class-H Registers - Headroom Control */ +#define WSA885X_CDC_CLSH_V1P8_BP_CTL1 0x86CD +#define WSA885X_CDC_CLSH_V1P8_BP_CTL0 0x86CC +#define WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0 0x86C7 +#define WSA885X_CDC_CLSH_CLSH_V_HD_PA 0x86C3 +#define WSA885X_CDC_CLSH_V1P8_BP_CTL2 0x86CE + +/* Driver Constants */ +#define WSA885X_CLK_RATE_FIXED 73728000 +#define WSA885X_NUM_REGS 0x03 + +/* Interrupt Registers */ +#define WSA885X_INTR_STATUS0 0x8584 +#define WSA885X_INTR_MASK0 0x8581 +#define WSA885X_INTR_CLEAR0 0x8587 + +/* Power and PA FSM Control Registers */ +#define WSA885X_PA0_FSM_CTL0 0x842A +#define WSA885X_PA1_FSM_CTL0 0x8434 + +/* Digital Control GPIO and Interrupt Registers */ +#define WSA885X_DIG_CTRL1_PIN_CT 0x8510 +#define WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL 0x8518 +#define WSA885X_DIG_CTRL1_INTR_MODE 0x8580 + +#define WSA885X_I2S_CTL0_PCM_RATE_MASK GENMASK(4, 1) +#define WSA885X_I2S_CTL0_ENABLE_MASK BIT(0) +#define WSA885X_I2S_CTL0_PCM_RATE(v) \ + FIELD_PREP(WSA885X_I2S_CTL0_PCM_RATE_MASK, (v)) +#define WSA885X_I2S_CTL0_PCM_RATE_8KHZ 0x0 +#define WSA885X_I2S_CTL0_PCM_RATE_16KHZ 0x1 +#define WSA885X_I2S_CTL0_PCM_RATE_32KHZ 0x2 +#define WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ 0x3 +#define WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ 0x4 +#define WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ 0x5 +#define WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ 0x6 +#define WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK GENMASK(2, 0) +#define WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK GENMASK(6, 4) +#define WSA885X_I2S_CFG0_TDM_TX_SLOT0(v) \ + FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, (v)) +#define WSA885X_I2S_CFG0_TDM_TX_SLOT1(v) \ + FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, (v)) +#define WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK GENMASK(2, 0) +#define WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK GENMASK(6, 4) +#define WSA885X_I2S_CFG1_TDM_TX_SLOT2(v) \ + FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, (v)) +#define WSA885X_I2S_CFG1_TDM_TX_SLOT3(v) \ + FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, (v)) +#define WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK BIT(0) +#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK GENMASK(3, 2) +#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2 \ + FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 0) +#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4 \ + FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 1) +#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8 \ + FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 3) +#define WSA885X_I2S_TDM_CH_TX_CH0_EN BIT(0) +#define WSA885X_I2S_TDM_CH_TX_CH1_EN BIT(1) +#define WSA885X_I2S_TDM_CH_TX_CH2_EN BIT(2) +#define WSA885X_I2S_TDM_CH_TX_CH3_EN BIT(3) +#define WSA885X_I2S_TDM_CH_RX_CH0_EN BIT(0) +#define WSA885X_I2S_TDM_CH_RX_CH3_EN BIT(3) +#define WSA885X_I2S_RESET_CTL_RESET_MASK BIT(0) +#define WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK BIT(2) +#define WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK BIT(3) +#define WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK BIT(2) + +#define WSA885X_I2S_TX_SLOT_ISENSE0 0x1 +#define WSA885X_I2S_TX_SLOT_ISENSE1 0x2 +#define WSA885X_I2S_TX_SLOT_CUR_SENSE0 0x5 +#define WSA885X_I2S_TX_SLOT_CUR_SENSE1 0x6 + +/* RX Sample Rate Index Values - Audio Playback Path */ +#define WSA885X_RX_RATE_8000HZ 0x00 +#define WSA885X_RX_RATE_16000HZ 0x01 +#define WSA885X_RX_RATE_32000HZ 0x02 +#define WSA885X_RX_RATE_44100HZ 0x03 +#define WSA885X_RX_RATE_48000HZ 0x04 +#define WSA885X_RX_RATE_96000HZ 0x05 +#define WSA885X_RX_RATE_192000HZ 0x06 +#define WSA885X_RX_RATE_384000HZ 0x07 + +/* VI Sample Rate Index Values - Voltage/Current Sensing Path */ +#define WSA885X_VI_RATE_8000HZ 0x00 +#define WSA885X_VI_RATE_16000HZ 0x01 +#define WSA885X_VI_RATE_44100HZ 0x02 +#define WSA885X_VI_RATE_48000HZ 0x03 +#define WSA885X_VI_RATE_96000HZ 0x04 +#define WSA885X_VI_RATE_22050HZ 0x05 +#define WSA885X_VI_RATE_24000HZ 0x06 +#define WSA885X_VI_RATE_192000HZ 0x07 +#define WSA885X_VI_RATE_384000HZ 0x08 + +/* Channel Configuration Masks */ +#define WSA885X_CHANNEL_STEREO 0x03 +#define WSA885X_CHANNEL_MONO_LEFT 0x01 +#define WSA885X_CHANNEL_MONO_RIGHT 0x02 + +#define WSA885X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define WSA885X_PLL_LOCK_BIT BIT(0) + +#define WSA885X_FU21_VOL_STEPS 124 +#define WSA885X_USAGE_MODE_MAX 8 +static const DECLARE_TLV_DB_SCALE(wsa885x_fu21_digital_gain, -8400, 100, 0); + +static bool wsa885x_is_valid_rx_slot_mask(u32 mask) +{ + return mask == WSA885X_CHANNEL_MONO_LEFT || + mask == WSA885X_CHANNEL_MONO_RIGHT || + mask == WSA885X_CHANNEL_STEREO; +} + +static const char *const wsa885x_supply_name[] = { + "vdd-1p8", + "vdd-io", +}; + +enum { + WSA885X_BATT_1S = 1, + WSA885X_BATT_2S, +}; + +enum { + WSA885X_IRQ_INT_SAF2WAR = 0, + WSA885X_IRQ_INT_WAR2SAF, + WSA885X_IRQ_INT_DISABLE, + WSA885X_IRQ_INT_PA0_OCP, + WSA885X_IRQ_INT_PA1_OCP, + WSA885X_IRQ_INT_CLIP0, + WSA885X_IRQ_INT_CLIP1, + WSA885X_IRQ_INT_CLK_WD, + WSA885X_IRQ_INT_INTR_GPIO1_PIN, + WSA885X_IRQ_INT_INTR_GPIO2_PIN, + WSA885X_IRQ_INT_UVLO, + WSA885X_IRQ_INT_BOP, + WSA885X_IRQ_INT_PA0_FSM_ERR, + WSA885X_IRQ_INT_PA1_FSM_ERR, + WSA885X_IRQ_INT_MAIN_FSM_ERR, + WSA885X_IRQ_INT_PCM_DATA0_WD, + WSA885X_IRQ_INT_PCM_DATA1_WD, + WSA885X_IRQ_INT_PCM_DATA0_DC, + WSA885X_IRQ_INT_PCM_DATA1_DC, + WSA885X_IRQ_INT_PLL_UNLOCKED, + WSA885X_IRQ_INT_PROT_MODE_CHANGE, + WSA885X_IRQ_INT_PB_CLOCK_VALID, + WSA885X_IRQ_INT_SENSE_CLOCK_VALID, + WSA885X_IRQ_MAX, +}; + +struct wsa885x_priv { + struct i2c_client *client; + struct regmap *regmap; + struct device *dev; + struct snd_soc_component *component; + struct gpio_desc *sd_n; + struct reset_control *sd_reset; + u32 usage_mode; + u32 rx_slot_mask; + u32 batt_conf; + int stereo_vol_db; + struct mutex state_lock; /* protects mutable control state */ +}; + +struct wsa885x_reg_update { + unsigned int reg; + unsigned int mask; + unsigned int val; +}; + +static const struct regmap_range_cfg wsa885x_regmap_ranges[] = { + { + .range_min = 0, + .range_max = 0x88ff, + .selector_reg = 0x0, + .selector_mask = 0xFF, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + }, +}; + +static const struct reg_default wsa885x_codec_reg_defaults[] = { + {WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01}, + {WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, 0x04}, + {WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, 0xac}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, 0xac}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03}, + {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, 0x03}, + {WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, 0x03}, + {WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x00}, + {WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x19}, + {WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x22}, + {WSA885X_ANA_PLL_DIV_CTL_0, 0x0c}, + {WSA885X_ANA_PLL_DIV_CTL_1, 0x50}, + {WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00}, + {WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xb4}, + {WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00}, + {WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00}, + {WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x03}, + {WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x3c}, + {WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc5}, + {WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x13}, + {WSA885X_ANA_TOP_BOOST_MISC, 0x79}, + {WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x00}, + {WSA885X_SPK_TOP_COMMON_CTRL2, 0x08}, + {WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x09}, + {WSA885X_SPK_TOP_LF_CH1_TUNE1, 0x00}, + {WSA885X_SPK_TOP_LF_CH2_TUNE1, 0x00}, + {WSA885X_SPK_TOP_LF_CH1_CTRL9, 0x00}, + {WSA885X_SPK_TOP_LF_CH2_CTRL9, 0x00}, + {WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x09}, + {WSA885X_SPK_TOP_COMMON_TUNE1, 0x03}, + {WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1, 0x00}, + {WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0e}, + {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x40}, + {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0xff}, + {WSA885X_ANA_TOP_PLL_STATUS_0, 0x00}, + {WSA885X_ANA_TOP_PLL_STATUS_1, 0x00}, + {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x84}, + {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x02}, + {WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x00}, + {WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0xe0}, + {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa4}, + {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0xc9}, + {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa4}, + {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0xc9}, + {WSA885X_DIG_CTRL0_TOP_CLK_CFG, 0x00}, + {WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x00}, + {WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00}, + {WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00}, + {WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00}, + {WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05}, + {WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x00}, + {WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x45}, + {WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x45}, + {WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x7f}, + {WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00}, + {WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01}, + {WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01}, + {WSA885X_DIG_CTRL1_I2S_CTL0, 0x06}, + {WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, 0x00}, + {WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, 0x00}, + {WSA885X_DIG_CTRL1_I2S_TDM_CTL0, 0x00}, + {WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x05}, + {WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, 0x00}, + {WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00}, + {WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, WSA885X_I2S_TDM_CH_RX_CH3_EN}, + {WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89}, + {WSA885X_CDC_RX0_RX_PATH_CFG1, 0x64}, + {WSA885X_CDC_RX0_RX_PATH_CTL, 0x24}, + {WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01}, + {WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89}, + {WSA885X_CDC_RX1_RX_PATH_CFG1, 0x64}, + {WSA885X_CDC_RX1_RX_PATH_CTL, 0x04}, + {WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01}, + {WSA885X_CDC_COMPANDER0_CTL0, 0x01}, + {WSA885X_CDC_COMPANDER0_CTL7, 0x2a}, + {WSA885X_CDC_COMPANDER1_CTL0, 0x01}, + {WSA885X_CDC_COMPANDER1_CTL7, 0x2a}, + {WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14}, + {WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14}, + {WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14}, + {WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14}, + {WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50}, + {WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c}, + {WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d}, + {WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03}, + {WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05}, +}; + +static void wsa885x_multi_update_bits(struct regmap *regmap, + const struct wsa885x_reg_update *updates, + size_t num_updates) +{ + size_t i; + + for (i = 0; i < num_updates; i++) + regmap_update_bits(regmap, updates[i].reg, + updates[i].mask, updates[i].val); +} + +static void wsa885x_toggle_irq_bit(struct wsa885x_priv *wsa885x, + unsigned int reg, unsigned int mask) +{ + regmap_update_bits(wsa885x->regmap, reg, mask, 0); + regmap_update_bits(wsa885x->regmap, reg, mask, mask); +} + +static void wsa885x_pulse_irq_bit(struct wsa885x_priv *wsa885x, + unsigned int reg, unsigned int mask) +{ + regmap_update_bits(wsa885x->regmap, reg, mask, 0); + regmap_update_bits(wsa885x->regmap, reg, mask, mask); + regmap_update_bits(wsa885x->regmap, reg, mask, 0); +} + +static int wsa885x_tdm_ctl0_slot_num_val(int slots, unsigned int *slot_num_val) +{ + if (!slot_num_val) + return -EINVAL; + + switch (slots) { + case 2: + *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2; + return 0; + case 4: + *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4; + return 0; + case 8: + *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8; + return 0; + default: + return -EINVAL; + } +} + +static int wsa885x_reg_update_sequence(struct regmap *regmap, int slots) +{ + static const struct reg_sequence regs[] = { + { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x15 }, + { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x11 }, + }; + unsigned int slot_num_val; + int ret; + + if (!regmap) + return -EINVAL; + + ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); + if (ret) + return ret; + + regmap_multi_reg_write(regmap, regs, ARRAY_SIZE(regs)); + + regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, + WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, + slot_num_val); + regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, + WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK, + WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK); + regmap_write(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, + WSA885X_I2S_TDM_CH_TX_CH0_EN); + regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, + WSA885X_I2S_TDM_CH_TX_CH1_EN, + WSA885X_I2S_TDM_CH_TX_CH1_EN); + + return 0; +} + +static int wsa885x_wait_for_pll_lock(struct wsa885x_priv *wsa885x) +{ + unsigned int status = 0; + int cnt = 0, ret = 0; + + do { + usleep_range(1000, 1100); + ret = regmap_read(wsa885x->regmap, WSA885X_ANA_TOP_PLL_STATUS_0, &status); + if (ret) { + dev_err(wsa885x->dev, "PLL status read failed: %d\n", ret); + return ret; + } + + if (status & WSA885X_PLL_LOCK_BIT) + return 0; + } while (++cnt < 20); + + dev_warn(wsa885x->dev, "PLL lock timeout after 20ms, status=0x%x\n", status); + return -ETIMEDOUT; +} + +static int wsa885x_2s_conf(struct wsa885x_priv *wsa885x) +{ + static const struct reg_sequence regs[] = { + { WSA885X_SPK_TOP_COMMON_TUNE1, 0x26 }, + { WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x0d }, + { WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x0d }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x71 }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0xAA }, + }; + + return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); +} + +static const struct reg_sequence wsa885x_reg_init[] = { + { WSA885X_CDC_RX0_RX_PATH_CTL, 0x24 }, + { WSA885X_CDC_RX1_RX_PATH_CTL, 0x24 }, + { WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01 }, + { WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01 }, + { WSA885X_CDC_COMPANDER0_CTL0, 0x01 }, + { WSA885X_CDC_COMPANDER1_CTL0, 0x01 }, + { WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14 }, + { WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14 }, + { WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14 }, + { WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14 }, + { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0f }, + { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x4f }, + { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x02 }, + { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00 }, + { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x01 }, + { WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x01 }, + { WSA885X_CDC_RX0_RX_PATH_CFG1, 0x60 }, + { WSA885X_CDC_RX1_RX_PATH_CFG1, 0x60 }, + { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa5 }, + { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa5 }, + { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x85 }, + { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0c }, + { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0e }, + { WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x0c }, + { WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01 }, + { WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01 }, + { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x88 }, + { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89 }, + { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x88 }, + { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89 }, + { WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x82 }, + { WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x34 }, + { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x41 }, + { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0x7f }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50 }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c }, + { WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d }, + { WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03 }, + { WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05 }, + { WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x20 }, + { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0x45 }, + { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0x45 }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05 }, + { WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x35 }, + { WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x21 }, + { WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc7 }, + { WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x11 }, + { WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0x80 }, + { WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x08 }, + { WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x47 }, + { WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x47 }, + { WSA885X_CDC_COMPANDER0_CTL7, 0x34 }, + { WSA885X_CDC_COMPANDER1_CTL7, 0x34 }, + { WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x79 }, +}; + +static int wsa885x_hw_init(struct wsa885x_priv *wsa885x) +{ + static const struct reg_sequence regs[] = { + { WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL, 0x2e }, + { WSA885X_DIG_CTRL1_INTR_MODE, 0x01 }, + { WSA885X_DIG_CTRL1_PIN_CT, 0x04 }, + }; + int ret; + + ret = regmap_multi_reg_write(wsa885x->regmap, wsa885x_reg_init, + ARRAY_SIZE(wsa885x_reg_init)); + if (ret) + return ret; + + if (wsa885x->batt_conf == WSA885X_BATT_2S) { + ret = wsa885x_2s_conf(wsa885x); + if (ret) + return ret; + } + + return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); +} + +static int wsa885x_unmask_interrupts(struct wsa885x_priv *wsa885x) +{ + static const struct reg_sequence regs[] = { + { WSA885X_INTR_MASK0, 0x00 }, + { WSA885X_INTR_MASK0 + 1, 0x00 }, + { WSA885X_INTR_MASK0 + 2, 0xf8 }, + }; + + return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); +} + +static int wsa885x_wait_for_pde_state(struct wsa885x_priv *wsa885x, int ps) +{ + unsigned int act_ps = 0, clock_valid = 0; + int rc = 0, cnt = 0; + + if (ps < 0 || ps > 3) + return -EINVAL; + + do { + usleep_range(1000, 1500); + rc = regmap_read(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, + &act_ps); + if (rc) { + dev_err(wsa885x->dev, "PDE state read failed: %d\n", rc); + return rc; + } + if (act_ps == ps) + return 0; + } while (++cnt < 5); + + if (regmap_read(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, + &clock_valid)) + dev_err(wsa885x->dev, + "PDE power state %d request failed, actual_ps %d, clock_valid read failed\n", + ps, act_ps); + else + dev_err(wsa885x->dev, + "PDE power state %d request failed, actual_ps %d, clock_valid:%d\n", + ps, act_ps, clock_valid); + + return -ETIMEDOUT; +} + +static void wsa885x_program_stereo_volume(struct wsa885x_priv *wsa885x, + int stereo_vol_db, bool commit) +{ + regmap_write(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, + (u8)(s8)stereo_vol_db); + regmap_write(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00); + regmap_write(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, + (u8)(s8)stereo_vol_db); + regmap_write(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00); + + if (commit) + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); +} + +static int wsa885x_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa885x_priv *wsa885x; + u8 pcm_rate, cs21_sample_rate_idx, cs24_sample_rate_idx; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + switch (params_rate(params)) { + case 8000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_8KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_8000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_8000HZ; + break; + case 16000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_16KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_16000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_16000HZ; + break; + case 32000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_32KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_32000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; + break; + case 44100: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_44100HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_44100HZ; + break; + case 48000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_48000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; + break; + case 88200: + case 96000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_96000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_96000HZ; + break; + case 176400: + case 192000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_192000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_192000HZ; + break; + case 352800: + case 384000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_384000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_384000HZ; + break; + default: + dev_err(wsa885x->dev, "sampling rate %d is not supported\n", params_rate(params)); + return -EINVAL; + } + + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, + WSA885X_I2S_CTL0_PCM_RATE_MASK | + WSA885X_I2S_CTL0_ENABLE_MASK, + WSA885X_I2S_CTL0_PCM_RATE(pcm_rate) | + WSA885X_I2S_CTL0_ENABLE_MASK); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00); + regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, + cs21_sample_rate_idx); + regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, + cs24_sample_rate_idx); + + mutex_lock(&wsa885x->state_lock); + wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); + mutex_unlock(&wsa885x->state_lock); + + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); + + return 0; +} + +static int wsa885x_codec_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_slot_mask, + unsigned int rx_slot_mask, int slots, + int slot_width) +{ + static const struct wsa885x_reg_update stereo_updates[] = { + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_ISENSE1) }, + { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, + WSA885X_I2S_CFG1_TDM_TX_SLOT2(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, + { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, + WSA885X_I2S_CFG1_TDM_TX_SLOT3(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, + }; + static const struct wsa885x_reg_update mono_left_updates[] = { + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, + }; + static const struct wsa885x_reg_update mono_right_updates[] = { + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE1) }, + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, + }; + struct wsa885x_priv *wsa885x; + unsigned int slot_num_val; + u32 mask; + int ret; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); + if (ret) { + dev_err(wsa885x->dev, "%s: unsupported slot count %d\n", + __func__, slots); + return ret; + } + + if (rx_slot_mask && !wsa885x_is_valid_rx_slot_mask(rx_slot_mask)) { + dev_err(wsa885x->dev, + "%s: unsupported rx_slot_mask 0x%x\n", + __func__, rx_slot_mask); + return -EINVAL; + } + + mutex_lock(&wsa885x->state_lock); + if (rx_slot_mask) + wsa885x->rx_slot_mask = rx_slot_mask; + else if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) + wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; + mask = wsa885x->rx_slot_mask; + + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, + WSA885X_I2S_RESET_CTL_RESET_MASK, + WSA885X_I2S_RESET_CTL_RESET_MASK); + + if (mask == WSA885X_CHANNEL_STEREO) { + wsa885x_multi_update_bits(wsa885x->regmap, stereo_updates, + ARRAY_SIZE(stereo_updates)); + ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); + if (ret) + goto exit_unlock; + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, + WSA885X_I2S_TDM_CH_TX_CH2_EN, + WSA885X_I2S_TDM_CH_TX_CH2_EN); + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, + WSA885X_I2S_TDM_CH_TX_CH3_EN, + WSA885X_I2S_TDM_CH_TX_CH3_EN); + } else if (mask == WSA885X_CHANNEL_MONO_LEFT) { + wsa885x_multi_update_bits(wsa885x->regmap, mono_left_updates, + ARRAY_SIZE(mono_left_updates)); + ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); + if (ret) + goto exit_unlock; + } else if (mask == WSA885X_CHANNEL_MONO_RIGHT) { + wsa885x_multi_update_bits(wsa885x->regmap, mono_right_updates, + ARRAY_SIZE(mono_right_updates)); + ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); + if (ret) + goto exit_unlock; + } + + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, + WSA885X_I2S_CTL0_ENABLE_MASK, + WSA885X_I2S_CTL0_ENABLE_MASK); + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, + WSA885X_I2S_RESET_CTL_RESET_MASK, 0); + + ret = 0; + +exit_unlock: + mutex_unlock(&wsa885x->state_lock); + + return ret; +} + +static int wsa885x_codec_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + static const struct reg_sequence pll_prep[] = { + { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x03 }, + { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x04 }, + { WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xB4 }, + { WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00 }, + { WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00 }, + }; + static const struct reg_sequence pll_cleanup[] = { + { WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00 }, + { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00 }, + { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00 }, + }; + struct wsa885x_priv *wsa885x; + u32 pll_div; + int ret = 0; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + if (!freq) + return -EINVAL; + if (WSA885X_CLK_RATE_FIXED % freq) + return -EINVAL; + pll_div = WSA885X_CLK_RATE_FIXED / freq; + if (pll_div > 0xff) + return -EINVAL; + + regmap_multi_reg_write(wsa885x->regmap, pll_prep, ARRAY_SIZE(pll_prep)); + regmap_write(wsa885x->regmap, WSA885X_ANA_PLL_DIV_CTL_0, pll_div); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x02); + + ret = wsa885x_wait_for_pll_lock(wsa885x); + if (ret) { + dev_err(wsa885x->dev, "PLL lock failed, aborting sysclk configuration\n"); + regmap_multi_reg_write(wsa885x->regmap, pll_cleanup, + ARRAY_SIZE(pll_cleanup)); + return ret; + } + + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x01); + regmap_write(wsa885x->regmap, WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00); + + return 0; +} + +static int wsa885x_codec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + static const struct reg_sequence mute_regs[] = { + { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, + { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03 }, + }; + static const struct reg_sequence mute_commit_regs[] = { + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01 }, + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01 }, + { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, + }; + static const struct reg_sequence unmute_prep_head_regs[] = { + { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, + }; + static const struct reg_sequence unmute_prep_tail_regs[] = { + { WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01 }, + { WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01 }, + }; + static const struct reg_sequence unmute_volume_regs[] = { + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00 }, + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00 }, + }; + static const struct reg_sequence unmute_commit_regs[] = { + { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, + { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x00 }, + }; + static const struct reg_sequence unmute_finish_regs[] = { + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x00 }, + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x00 }, + { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, + }; + struct wsa885x_priv *wsa885x; + int ret = 0, ps0 = 0, ps3 = 3; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + mutex_lock(&wsa885x->state_lock); + + if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { + ret = -EINVAL; + goto exit_unlock; + } + + if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) + wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; + + if (mute) { + regmap_multi_reg_write(wsa885x->regmap, mute_regs, + ARRAY_SIZE(mute_regs)); + ret = wsa885x_wait_for_pde_state(wsa885x, ps3); + if (ret) { + dev_err(wsa885x->dev, + "PS3 transition failed: %d\n", ret); + } else { + regmap_multi_reg_write(wsa885x->regmap, mute_commit_regs, + ARRAY_SIZE(mute_commit_regs)); + } + } else { + regmap_multi_reg_write(wsa885x->regmap, unmute_prep_head_regs, + ARRAY_SIZE(unmute_prep_head_regs)); + regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, + wsa885x->usage_mode); + regmap_multi_reg_write(wsa885x->regmap, unmute_prep_tail_regs, + ARRAY_SIZE(unmute_prep_tail_regs)); + wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); + regmap_multi_reg_write(wsa885x->regmap, unmute_volume_regs, + ARRAY_SIZE(unmute_volume_regs)); + regmap_multi_reg_write(wsa885x->regmap, unmute_commit_regs, + ARRAY_SIZE(unmute_commit_regs)); + ret = wsa885x_wait_for_pde_state(wsa885x, ps0); + if (ret) + goto exit_unlock; + + if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_STEREO) { + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, + WSA885X_I2S_TDM_CH_RX_CH0_EN | + WSA885X_I2S_TDM_CH_RX_CH3_EN); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x03); + } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_LEFT) { + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, + WSA885X_I2S_TDM_CH_RX_CH3_EN); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x01); + } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_RIGHT) { + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, + WSA885X_I2S_TDM_CH_RX_CH0_EN); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x02); + } + + regmap_multi_reg_write(wsa885x->regmap, unmute_finish_regs, + ARRAY_SIZE(unmute_finish_regs)); + } + +exit_unlock: + mutex_unlock(&wsa885x->state_lock); + + return ret; +} + +static int wsa885x_codec_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + static const struct reg_sequence regs[] = { + { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, + }; + struct wsa885x_priv *wsa885x; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + mutex_lock(&wsa885x->state_lock); + regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); + mutex_unlock(&wsa885x->state_lock); + + return 0; +} + +static const struct snd_soc_dai_ops wsa885x_dai_ops = { + .hw_params = wsa885x_codec_hw_params, + .set_tdm_slot = wsa885x_codec_set_tdm_slot, + .set_sysclk = wsa885x_codec_set_sysclk, + .mute_stream = wsa885x_codec_mute_stream, + .hw_free = wsa885x_codec_hw_free, +}; + +static struct snd_soc_dai_driver wsa885x_dai[] = { + { + .name = "wsa885x_dai_drv", + .playback = { + .stream_name = "WSA885X TDM Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WSA885X_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &wsa885x_dai_ops, + }, +}; + +static void wsa885x_reset_assert(void *data) +{ + struct wsa885x_priv *wsa885x = data; + + if (wsa885x->sd_reset) + reset_control_assert(wsa885x->sd_reset); + else + gpiod_direction_output(wsa885x->sd_n, 1); +} + +static void wsa885x_reset_deassert(struct wsa885x_priv *wsa885x) +{ + if (wsa885x->sd_reset) + reset_control_deassert(wsa885x->sd_reset); + else + gpiod_direction_output(wsa885x->sd_n, 0); +} + +static int wsa885x_get_reset(struct device *dev, struct wsa885x_priv *wsa885x) +{ + wsa885x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(wsa885x->sd_reset)) + return dev_err_probe(dev, PTR_ERR(wsa885x->sd_reset), + "Failed to get reset\n"); + else if (wsa885x->sd_reset) + return 0; + + wsa885x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(wsa885x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa885x->sd_n), + "Shutdown Control GPIO not found\n"); + + return 0; +} + +static bool wsa885x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA885X_ANA_TOP_PLL_STATUS_0: + case WSA885X_ANA_TOP_PLL_STATUS_1: + case WSA885X_DIG_CTRL0_SDCA_COMMIT: + case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: + case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: + case WSA885X_INTR_STATUS0: + case WSA885X_INTR_STATUS0 + 1: + case WSA885X_INTR_STATUS0 + 2: + case WSA885X_INTR_CLEAR0: + case WSA885X_INTR_CLEAR0 + 1: + case WSA885X_INTR_CLEAR0 + 2: + return true; + default: + return false; + } +} + +static bool wsa885x_readable_register(struct device *dev, unsigned int reg) +{ + if (reg == WSA885X_INTR_CLEAR0 || + reg == WSA885X_INTR_CLEAR0 + 1 || + reg == WSA885X_INTR_CLEAR0 + 2) + return false; + return reg <= 0x88ff; +} + +static bool wsa885x_writeable_register(struct device *dev, unsigned int reg) +{ + if (reg > 0x88ff) + return false; + + switch (reg) { + case WSA885X_ANA_TOP_PLL_STATUS_0: + case WSA885X_ANA_TOP_PLL_STATUS_1: + case WSA885X_INTR_STATUS0: + case WSA885X_INTR_STATUS0 + 1: + case WSA885X_INTR_STATUS0 + 2: + case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: + case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: + return false; + default: + return true; + } +} + +static const struct regmap_config wsa885x_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x88FF, + .ranges = wsa885x_regmap_ranges, + .num_ranges = ARRAY_SIZE(wsa885x_regmap_ranges), + .reg_defaults = wsa885x_codec_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wsa885x_codec_reg_defaults), + .volatile_reg = wsa885x_volatile_register, + .writeable_reg = wsa885x_writeable_register, + .readable_reg = wsa885x_readable_register, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int wsa885x_component_probe(struct snd_soc_component *component) +{ + struct wsa885x_priv *wsa885x = + snd_soc_component_get_drvdata(component); + int ret; + + wsa885x->component = component; + snd_soc_component_init_regmap(component, wsa885x->regmap); + + ret = wsa885x_hw_init(wsa885x); + if (ret) + return ret; + + return wsa885x_unmask_interrupts(wsa885x); +} + +static int wsa885x_stereo_gain_offset_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + int val; + + mutex_lock(&wsa885x->state_lock); + val = wsa885x->stereo_vol_db + 84; + mutex_unlock(&wsa885x->state_lock); + if (val < 0 || val > WSA885X_FU21_VOL_STEPS) + return -ERANGE; + + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int wsa885x_stereo_gain_offset_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + long val; + int stereo_vol_db; + + val = ucontrol->value.integer.value[0]; + + if (val < 0 || val > WSA885X_FU21_VOL_STEPS) { + dev_err(component->dev, "%s: Invalid range, Val: %ld\n", __func__, val); + return -EINVAL; + } + + stereo_vol_db = (int)val - 84; + + mutex_lock(&wsa885x->state_lock); + if (wsa885x->stereo_vol_db == stereo_vol_db) { + mutex_unlock(&wsa885x->state_lock); + return 0; + } + + wsa885x_program_stereo_volume(wsa885x, stereo_vol_db, true); + wsa885x->stereo_vol_db = stereo_vol_db; + mutex_unlock(&wsa885x->state_lock); + + return 1; +} + +static int wsa885x_usage_modes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + + mutex_lock(&wsa885x->state_lock); + if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { + mutex_unlock(&wsa885x->state_lock); + return -ERANGE; + } + + ucontrol->value.integer.value[0] = wsa885x->usage_mode; + mutex_unlock(&wsa885x->state_lock); + + return 0; +} + +static int wsa885x_usage_modes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + u32 val = ucontrol->value.integer.value[0]; + + if (val > WSA885X_USAGE_MODE_MAX) + return -EINVAL; + + mutex_lock(&wsa885x->state_lock); + if (wsa885x->usage_mode == val) { + mutex_unlock(&wsa885x->state_lock); + return 0; + } + + wsa885x->usage_mode = val; + mutex_unlock(&wsa885x->state_lock); + + return 1; +} + +static int wsa885x_rx_slot_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + u32 mask; + + mutex_lock(&wsa885x->state_lock); + mask = wsa885x->rx_slot_mask; + mutex_unlock(&wsa885x->state_lock); + if (!wsa885x_is_valid_rx_slot_mask(mask)) + return -ERANGE; + + ucontrol->value.integer.value[0] = mask; + + return 0; +} + +static int wsa885x_rx_slot_mask_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + u32 mask = ucontrol->value.integer.value[0]; + + if (!wsa885x_is_valid_rx_slot_mask(mask)) + return -EINVAL; + + mutex_lock(&wsa885x->state_lock); + if (wsa885x->rx_slot_mask == mask) { + mutex_unlock(&wsa885x->state_lock); + return 0; + } + + wsa885x->rx_slot_mask = mask; + mutex_unlock(&wsa885x->state_lock); + + return 1; +} + +static const struct snd_kcontrol_new wsa885x_snd_controls[] = { + SOC_SINGLE_EXT("Usage Mode", SND_SOC_NOPM, 0, WSA885X_USAGE_MODE_MAX, 0, + wsa885x_usage_modes_get, + wsa885x_usage_modes_put), + + SOC_SINGLE_EXT_TLV("Speaker Volume", SND_SOC_NOPM, + 0, WSA885X_FU21_VOL_STEPS, 0, + wsa885x_stereo_gain_offset_get, + wsa885x_stereo_gain_offset_put, + wsa885x_fu21_digital_gain), + + SOC_SINGLE_EXT("Rx Slot Mask", SND_SOC_NOPM, 0, 3, 0, + wsa885x_rx_slot_mask_get, + wsa885x_rx_slot_mask_put), +}; + +static const struct snd_soc_component_driver wsa885x_component = { + .name = "wsa885x", + .probe = wsa885x_component_probe, + .controls = wsa885x_snd_controls, + .num_controls = ARRAY_SIZE(wsa885x_snd_controls), +}; + +static irqreturn_t wsa885x_handle_irq(int irq_idx, void *data) +{ + struct wsa885x_priv *wsa885x = data; + + if (irq_idx < 0 || irq_idx >= WSA885X_IRQ_MAX) + return IRQ_NONE; + + switch (irq_idx) { + case WSA885X_IRQ_INT_SAF2WAR: + case WSA885X_IRQ_INT_WAR2SAF: + case WSA885X_IRQ_INT_DISABLE: + case WSA885X_IRQ_INT_INTR_GPIO1_PIN: + case WSA885X_IRQ_INT_INTR_GPIO2_PIN: + case WSA885X_IRQ_INT_PA0_OCP: + case WSA885X_IRQ_INT_PA1_OCP: + case WSA885X_IRQ_INT_CLIP0: + case WSA885X_IRQ_INT_CLIP1: + case WSA885X_IRQ_INT_CLK_WD: + case WSA885X_IRQ_INT_BOP: + case WSA885X_IRQ_INT_UVLO: + case WSA885X_IRQ_INT_PCM_DATA0_DC: + case WSA885X_IRQ_INT_PCM_DATA1_DC: + case WSA885X_IRQ_INT_PLL_UNLOCKED: + case WSA885X_IRQ_INT_PROT_MODE_CHANGE: + case WSA885X_IRQ_INT_PB_CLOCK_VALID: + case WSA885X_IRQ_INT_SENSE_CLOCK_VALID: + break; + case WSA885X_IRQ_INT_PCM_DATA0_WD: + case WSA885X_IRQ_INT_PCM_DATA1_WD: + if (irq_idx == WSA885X_IRQ_INT_PCM_DATA0_WD) + wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1, + WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); + else + wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1, + WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); + break; + case WSA885X_IRQ_INT_PA0_FSM_ERR: + case WSA885X_IRQ_INT_PA1_FSM_ERR: + case WSA885X_IRQ_INT_MAIN_FSM_ERR: + if (irq_idx == WSA885X_IRQ_INT_MAIN_FSM_ERR) { + wsa885x_pulse_irq_bit(wsa885x, WSA885X_DIG_CTRL0_POWER_FSM_CTL0, + WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK); + } else if (irq_idx == WSA885X_IRQ_INT_PA0_FSM_ERR) { + wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA0_FSM_CTL0, + WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); + } else if (irq_idx == WSA885X_IRQ_INT_PA1_FSM_ERR) { + wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA1_FSM_CTL0, + WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); + } + break; + default: + break; + } + + return IRQ_HANDLED; +} + +static irqreturn_t wsa885x_interrupt_handler(int irq, void *data) +{ + static const unsigned int status_reg[WSA885X_NUM_REGS] = { + WSA885X_INTR_STATUS0, + WSA885X_INTR_STATUS0 + 1, + WSA885X_INTR_STATUS0 + 2, + }; + static const unsigned int clear_reg[WSA885X_NUM_REGS] = { + WSA885X_INTR_CLEAR0, + WSA885X_INTR_CLEAR0 + 1, + WSA885X_INTR_CLEAR0 + 2, + }; + unsigned int status[WSA885X_NUM_REGS] = { 0 }; + struct wsa885x_priv *wsa885x = data; + irqreturn_t handled = IRQ_NONE; + irqreturn_t irq_ret; + int i, bit, ret, irq_num; + + for (i = 0; i < WSA885X_NUM_REGS; i++) { + ret = regmap_read(wsa885x->regmap, status_reg[i], &status[i]); + if (ret) { + dev_err(wsa885x->dev, + "Failed to read status_reg[%d] (0x%x): %d\n", + i, status_reg[i], ret); + status[i] = 0; + continue; + } + } + + for (i = 0; i < WSA885X_NUM_REGS; i++) { + for (bit = 0; bit < 8; bit++) { + if (status[i] & BIT(bit)) { + irq_num = i * 8 + bit; + regmap_write(wsa885x->regmap, clear_reg[i], BIT(bit)); + regmap_write(wsa885x->regmap, clear_reg[i], 0); + if (irq_num >= WSA885X_IRQ_MAX) { + dev_warn_ratelimited(wsa885x->dev, + "Unexpected IRQ bit %d (reg %d)\n", + bit, i); + handled = IRQ_HANDLED; + continue; + } + irq_ret = wsa885x_handle_irq(irq_num, wsa885x); + if (irq_ret == IRQ_HANDLED) + handled = IRQ_HANDLED; + } + } + } + return handled; +} + +static int wsa885x_register_irq(struct wsa885x_priv *wsa885x) +{ + if (!wsa885x->client->irq) + return dev_err_probe(wsa885x->dev, -EINVAL, + "IRQ is not configured\n"); + + return devm_request_threaded_irq(wsa885x->dev, wsa885x->client->irq, NULL, + wsa885x_interrupt_handler, + IRQF_ONESHOT, + dev_name(wsa885x->dev), wsa885x); +} + +static int wsa885x_probe(struct i2c_client *client) +{ + struct wsa885x_priv *wsa885x; + const struct snd_soc_component_driver *component_driver = &wsa885x_component; + const char *battery_config; + unsigned int i; + int ret; + struct device *dev = &client->dev; + + wsa885x = devm_kzalloc(dev, sizeof(*wsa885x), GFP_KERNEL); + if (!wsa885x) + return -ENOMEM; + + wsa885x->client = client; + wsa885x->dev = dev; + wsa885x->stereo_vol_db = -84; + wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; + mutex_init(&wsa885x->state_lock); + wsa885x->regmap = devm_regmap_init_i2c(client, &wsa885x_regmap_cfg); + + if (IS_ERR(wsa885x->regmap)) + return PTR_ERR(wsa885x->regmap); + + ret = device_property_read_string(dev, "qcom,battery-config", + &battery_config); + if (ret) { + wsa885x->batt_conf = WSA885X_BATT_1S; + } else if (!strcmp(battery_config, "1s")) { + wsa885x->batt_conf = WSA885X_BATT_1S; + } else if (!strcmp(battery_config, "2s")) { + wsa885x->batt_conf = WSA885X_BATT_2S; + } else { + return dev_err_probe(dev, -EINVAL, + "Invalid battery config %s (expected 1s or 2s)\n", + battery_config); + } + + for (i = 0; i < ARRAY_SIZE(wsa885x_supply_name); i++) { + ret = devm_regulator_get_enable(dev, wsa885x_supply_name[i]); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable regulator %s\n", + wsa885x_supply_name[i]); + } + + ret = wsa885x_get_reset(dev, wsa885x); + if (ret) + return ret; + + wsa885x_reset_deassert(wsa885x); + usleep_range(5000, 5500); + + ret = devm_add_action_or_reset(dev, wsa885x_reset_assert, wsa885x); + if (ret) + return dev_err_probe(dev, ret, "devm_add_action_or_reset failed\n"); + + i2c_set_clientdata(client, wsa885x); + + ret = wsa885x_register_irq(wsa885x); + if (ret) + return dev_err_probe(dev, ret, "wsa885x irq registration failed\n"); + + ret = devm_snd_soc_register_component(dev, component_driver, + wsa885x_dai, + ARRAY_SIZE(wsa885x_dai)); + if (ret) + return dev_err_probe(dev, ret, "Codec component registration failed\n"); + + return 0; +} + +static const struct of_device_id wsa885x_dt_match[] = { + { + .compatible = "qcom,wsa8855", + }, + {} +}; +MODULE_DEVICE_TABLE(of, wsa885x_dt_match); + +static const struct i2c_device_id wsa885x_id[] = { + { + .name = "wsa885x", + .driver_data = 0, + }, + {} +}; +MODULE_DEVICE_TABLE(i2c, wsa885x_id); + +static struct i2c_driver wsa885x_driver = { + .driver = { + .name = "wsa885x", + .of_match_table = wsa885x_dt_match, + }, + .probe = wsa885x_probe, + .id_table = wsa885x_id, +}; + +module_i2c_driver(wsa885x_driver); + +MODULE_DESCRIPTION("ASoC WSA885X Stereo Smart PA Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c index 7de4014e626d..894b8eb42e39 100644 --- a/sound/soc/codecs/zl38060.c +++ b/sound/soc/codecs/zl38060.c @@ -162,7 +162,7 @@ static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data) static int zl38_load_firmware(struct device *dev, struct regmap *regmap) { const struct ihex_binrec *rec; - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; u32 addr; u16 len; int err; @@ -180,7 +180,7 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) return err; err = zl38_fw_enter_boot_mode(regmap); if (err) - goto out; + return err; rec = (const struct ihex_binrec *)fw->data; while (rec) { addr = be32_to_cpu(rec->addr); @@ -195,15 +195,12 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) err = -EINVAL; } if (err) - goto out; + return err; /* next ! */ rec = ihex_next_binrec(rec); } - err = zl38_fw_go(regmap); -out: - release_firmware(fw); - return err; + return zl38_fw_go(regmap); } diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 74dfd39fd604..2e9fb79b1cd8 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -955,7 +955,9 @@ static int dw_i2s_probe(struct platform_device *pdev) } irq = platform_get_irq_optional(pdev, 0); - if (irq >= 0) { + if (irq == -EPROBE_DEFER) + return irq; + if (irq > 0) { ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0, pdev->name, dev); if (ret < 0) { diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 9dc46ab1d3e1..310ee4c282a1 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -69,7 +69,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { /** * struct codec_priv - CODEC private data - * @mclk: Main clock of the CODEC * @mclk_freq: Clock rate of MCLK * @free_freq: Clock rate of MCLK for hw_free() * @mclk_id: MCLK (or main clock) id for set_sysclk() @@ -80,7 +79,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { * to stay within PLL frequency limits */ struct codec_priv { - struct clk *mclk; unsigned long mclk_freq; unsigned long free_freq; u32 mclk_id; @@ -109,12 +107,58 @@ struct cpu_priv { u32 slot_num; }; +struct fsl_asoc_card_priv; + +/* + * struct fsl_asoc_card_pdata - per-compatible static card description + * @sysclk_dir: initial CPU SYSCLK direction override (0 = leave default IN) + * @sysclk_ratio: SYSCLK ratio on sample rate (0 = not used) + * @slot_width: TDM slot width (0 = not TDM) + * @codec_dai_name: name of the codec DAI + * @codec_mclk_id: MCLK id passed to set_sysclk() for the codec + * @codec_fll_id: FLL id; only valid when has_pll is true + * @codec_pll_id: PLL id; only valid when has_pll is true + * @codec_pll_ratio_s24: PLL output ratio for S24_LE + * @has_pll: codec uses PLL/FLL; codec_fll_id and codec_pll_id are valid + * @dai_fmt: DAI format flags + * @playback_only: restrict card to playback direction + * @capture_only: restrict card to capture direction + * @dapm_routes: DAPM route table override + * @num_dapm_routes: number of entries in dapm_routes + * @exclude_format: PCM format bitmask excluded (for SAI + WM8960/WM8962) + * @codec_init: codec-specific init run after mclk_freq is populated + * @probe_init: optional DT-driven init run at end of probe() (e.g. SPDIF codec discovery) + */ +struct fsl_asoc_card_pdata { + u32 sysclk_dir[2]; + u32 sysclk_ratio[2]; + u32 slot_width; + const char *codec_dai_name; + u32 codec_mclk_id; + int codec_fll_id; + int codec_pll_id; + int codec_pll_ratio_s24; + bool has_pll; + bool playback_only; + bool capture_only; + u32 dai_fmt; + const struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + u64 exclude_format; + int (*codec_init)(struct fsl_asoc_card_priv *priv); + int (*probe_init)(struct device_node *codec_np[], + struct device_node *cpu_np, + const char *codec_dai_name[], + struct fsl_asoc_card_priv *priv); +}; + /** * struct fsl_asoc_card_priv - Freescale Generic ASOC card private data * @dai_link: DAI link structure including normal one and DPCM link * @hp_jack: Headphone Jack structure * @mic_jack: Microphone Jack structure * @pdev: platform device pointer + * @pdata: pointer to the per-compatible card platform data * @codec_priv: CODEC private data * @cpu_priv: CPU private data * @card: ASoC card structure @@ -135,6 +179,7 @@ struct fsl_asoc_card_priv { struct simple_util_jack hp_jack; struct simple_util_jack mic_jack; struct platform_device *pdev; + const struct fsl_asoc_card_pdata *pdata; struct codec_priv codec_priv[2]; struct cpu_priv cpu_priv; struct snd_soc_card card; @@ -609,6 +654,192 @@ static int fsl_asoc_card_spdif_init(struct device_node *codec_np[], return 0; } +static int fsl_asoc_card_cs42888_codec_init(struct fsl_asoc_card_priv *priv) +{ + unsigned long mclk_freq = priv->codec_priv[0].mclk_freq; + + /* + * Set CPU sysclk frequency from codec MCLK only if not already + * set by the CPU DAI init (e.g. ESAI extal clock takes precedence). + */ + if (!priv->cpu_priv.sysclk_freq[TX]) + priv->cpu_priv.sysclk_freq[TX] = mclk_freq; + if (!priv->cpu_priv.sysclk_freq[RX]) + priv->cpu_priv.sysclk_freq[RX] = mclk_freq; + + priv->constraint_channels = &cs42888_channel_constraints; + if (mclk_freq % 12288000 == 0) + priv->constraint_rates = &cs42888_rate_48k_constraints; + else if (mclk_freq % 11289600 == 0) + priv->constraint_rates = &cs42888_rate_44k_constraints; + else + dev_warn(&priv->pdev->dev, + "Unknown MCLK frequency %lu, no rate constraints\n", + mclk_freq); + + return 0; +} + +static int fsl_asoc_card_wm8958_codec_init(struct fsl_asoc_card_priv *priv) +{ + priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; + return 0; +} + +static const struct fsl_asoc_card_pdata fsl_asoc_cs42888_pdata = { + .codec_dai_name = "cs42888", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, + .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, + .slot_width = 32, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + .codec_init = fsl_asoc_card_cs42888_codec_init, +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_cs427x_pdata = { + .codec_dai_name = "cs4271-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = CS427x_SYSCLK_MCLK, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_sgtl5000_pdata = { + .codec_dai_name = "sgtl5000", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = SGTL5000_SYSCLK, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic32x4_pdata = { + .codec_dai_name = "tlv320aic32x4-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic31xx_pdata = { + .codec_dai_name = "tlv320dac31xx-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, + .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, + .playback_only = true, + .dapm_routes = audio_map_tx, + .num_dapm_routes = ARRAY_SIZE(audio_map_tx), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8962_pdata = { + .codec_dai_name = "wm8962", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = WM8962_SYSCLK_MCLK, + .has_pll = true, + .codec_fll_id = WM8962_SYSCLK_FLL, + .codec_pll_id = WM8962_FLL, + /* + * WM8962 has same BCLK generation limitations as WM8960. + * See WM8960 section for detailed explanation. + */ + .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8960_pdata = { + .codec_dai_name = "wm8960-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .has_pll = true, + .codec_fll_id = WM8960_SYSCLK_AUTO, + .codec_pll_id = WM8960_SYSCLK_AUTO, + /* + * WM8960 in master mode cannot generate exact 1.92 MHz BCLK + * required for S20_3LE (48kHz x 2ch x 20bit). Closest available + * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. + * + * In SAI master mode, SAI derive BCLK from MCLK using integer + * dividers only. S20_3LE requires non-integer divider ratios + * with standard MCLK frequencies. For example, 48kHz stereo + * needs 1.920 MHz BCLK, which requires a divider of 6.4 from + * 12.288 MHz MCLK (not an integer). + */ + .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_ac97_pdata = { + .codec_dai_name = "ac97-hifi", + .dai_fmt = SND_SOC_DAIFMT_AC97, + .dapm_routes = audio_map_ac97, + .num_dapm_routes = ARRAY_SIZE(audio_map_ac97), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_mqs_pdata = { + .codec_dai_name = "fsl-mqs-dai", + .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC | + SND_SOC_DAIFMT_NB_NF, + .playback_only = true, + .dapm_routes = audio_map_tx, + .num_dapm_routes = ARRAY_SIZE(audio_map_tx), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8524_pdata = { + .codec_dai_name = "wm8524-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, + /* RX=0, TX=1: set TX (index 1) to CLOCK_OUT, RX stays at default IN */ + .sysclk_dir = { 0, SND_SOC_CLOCK_OUT }, + .sysclk_ratio = { 0, 256 }, + .slot_width = 32, + .playback_only = true, + .dapm_routes = audio_map_tx, + .num_dapm_routes = ARRAY_SIZE(audio_map_tx), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_si476x_pdata = { + .codec_dai_name = "si476x-codec", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, + .dapm_routes = audio_map_rx, + .num_dapm_routes = ARRAY_SIZE(audio_map_rx), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8958_pdata = { + .codec_dai_name = "wm8994-aif1", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = WM8994_FLL_SRC_MCLK1, + .has_pll = true, + .codec_fll_id = WM8994_SYSCLK_FLL1, + .codec_pll_id = WM8994_FLL1, + .codec_init = fsl_asoc_card_wm8958_codec_init, +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_nau8822_pdata = { + .codec_dai_name = "nau8822-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = NAU8822_CLK_MCLK, + .has_pll = true, + .codec_fll_id = NAU8822_CLK_PLL, + .codec_pll_id = NAU8822_CLK_PLL, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8904_pdata = { + .codec_dai_name = "wm8904-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = WM8904_FLL_MCLK, + .has_pll = true, + .codec_fll_id = WM8904_CLK_FLL, + .codec_pll_id = WM8904_FLL_MCLK, + .codec_pll_ratio_s24 = 192, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_spdif_pdata = { + .codec_dai_name = "spdif", + .dai_fmt = DAI_FMT_BASE, + .probe_init = fsl_asoc_card_spdif_init, +}; + static int hp_jack_event(struct notifier_block *nb, unsigned long event, void *data) { @@ -643,17 +874,173 @@ static struct notifier_block mic_jack_nb = { .notifier_call = mic_jack_event, }; -static int fsl_asoc_card_late_probe(struct snd_soc_card *card) +/* + * fsl_asoc_card_init_cpu - configure CPU DAI-specific settings. + * + * Called from late_probe() when the CPU DAI component is guaranteed bound. + */ +static int fsl_asoc_card_init_cpu(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) { struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_pcm_runtime *rtd = list_first_entry( - &card->rtd_list, struct snd_soc_pcm_runtime, list); + struct device_node *np = priv->pdev->dev.of_node; + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + const char *comp_drv_name = cpu_dai->component->driver->name; + struct device *dev = card->dev; + int ret; + + if (!strcmp(comp_drv_name, "fsl-ssi")) { + /* Only SSI needs to configure AUDMUX */ + ret = fsl_asoc_card_audmux_init(np, priv); + if (ret) { + dev_err(dev, "failed to init audmux\n"); + return ret; + } + } else if (!strcmp(comp_drv_name, "fsl-esai")) { + struct clk *esai_clk = clk_get(cpu_dai->dev, "extal"); + + if (!IS_ERR(esai_clk)) { + priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); + priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); + clk_put(esai_clk); + } else { + dev_warn(dev, "failed to get ESAI extal clock: %ld\n", PTR_ERR(esai_clk)); + } + + priv->cpu_priv.sysclk_id[TX] = ESAI_HCKT_EXTAL; + priv->cpu_priv.sysclk_id[RX] = ESAI_HCKR_EXTAL; + } else if (!strcmp(comp_drv_name, "fsl-sai")) { + priv->cpu_priv.sysclk_id[TX] = FSL_SAI_CLK_MAST1; + priv->cpu_priv.sysclk_id[RX] = FSL_SAI_CLK_MAST1; + + if (priv->pdata->exclude_format) + priv->exclude_format = priv->pdata->exclude_format; + } + + return 0; +} + +/* + * fsl_asoc_card_init_codecs - read codec MCLK rates and set codec sysclk. + * + * Called from late_probe() after all components are bound. + */ +static int fsl_asoc_card_init_codecs(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + const struct fsl_asoc_card_pdata *pdata = priv->pdata; struct snd_soc_dai *codec_dai; struct codec_priv *codec_priv; struct device *dev = card->dev; int codec_idx; int ret; + /* Read MCLK rate from each bound codec component */ + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + struct clk *codec_clk = clk_get(codec_dai->component->dev, NULL); + + codec_priv = &priv->codec_priv[codec_idx]; + if (!IS_ERR(codec_clk)) { + codec_priv->mclk_freq = clk_get_rate(codec_clk); + clk_put(codec_clk); + } + } + + if (pdata->codec_init) { + ret = pdata->codec_init(priv); + if (ret) + return ret; + } + + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + codec_priv = &priv->codec_priv[codec_idx]; + + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, + codec_priv->mclk_freq, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set sysclk in %s\n", __func__); + return ret; + } + } + + return 0; +} + +static void fsl_asoc_card_free_jack(struct snd_soc_card *card) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + + if (priv->hp_jack.gpio.desc) { + snd_soc_jack_notifier_unregister(&priv->hp_jack.jack, &hp_jack_nb); + snd_soc_jack_free_gpios(&priv->hp_jack.jack, 1, &priv->hp_jack.gpio); + priv->hp_jack.gpio.desc = NULL; + } + + if (priv->mic_jack.gpio.desc) { + snd_soc_jack_notifier_unregister(&priv->mic_jack.jack, &mic_jack_nb); + snd_soc_jack_free_gpios(&priv->mic_jack.jack, 1, &priv->mic_jack.gpio); + priv->mic_jack.gpio.desc = NULL; + } +} + +/* + * fsl_asoc_card_init_jack - register optional headphone and mic jacks. + * + * Called from late_probe() once per card bind cycle. + */ +static int fsl_asoc_card_init_jack(struct snd_soc_card *card) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + struct device_node *np = priv->pdev->dev.of_node; + int ret; + + /* + * Properties "hp-det-gpios" and "mic-det-gpios" are optional. + * simple_util_init_jack() checks for the GPIO property and + * does nothing if it is absent. + */ + if (of_property_present(np, "hp-det-gpios") || + of_property_present(np, "hp-det-gpio") /* deprecated */) { + ret = simple_util_init_jack(card, &priv->hp_jack, + 1, NULL, "Headphone Jack"); + if (ret) + return ret; + + snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); + } + + if (of_property_present(np, "mic-det-gpios") || + of_property_present(np, "mic-det-gpio") /* deprecated */) { + ret = simple_util_init_jack(card, &priv->mic_jack, + 0, NULL, "Mic Jack"); + if (ret) + return ret; + + snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); + } + + return 0; +} + +static int fsl_asoc_card_late_probe(struct snd_soc_card *card) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_pcm_runtime *rtd; + int ret; + + /* Use the first rtd which carries the CPU+codec DAIs */ + rtd = list_first_entry(&card->rtd_list, + struct snd_soc_pcm_runtime, list); + + ret = fsl_asoc_card_init_jack(card); + if (ret) + goto jack_fail; + + ret = fsl_asoc_card_init_cpu(card, rtd); + if (ret) + goto jack_fail; + if (fsl_asoc_card_is_ac97(priv)) { #if IS_ENABLED(CONFIG_SND_AC97_CODEC) struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; @@ -671,19 +1058,20 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) return 0; } - for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { - codec_priv = &priv->codec_priv[codec_idx]; + ret = fsl_asoc_card_init_codecs(card, rtd); + if (ret) + goto jack_fail; - ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, - codec_priv->mclk_freq, SND_SOC_CLOCK_IN); - if (ret && ret != -ENOTSUPP) { - dev_err(dev, "failed to set sysclk in %s\n", __func__); - return ret; - } + return 0; - if (!IS_ERR_OR_NULL(codec_priv->mclk)) - clk_prepare_enable(codec_priv->mclk); - } +jack_fail: + fsl_asoc_card_free_jack(card); + return ret; +} + +static int fsl_asoc_card_card_remove(struct snd_soc_card *card) +{ + fsl_asoc_card_free_jack(card); return 0; } @@ -697,12 +1085,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct platform_device *asrc_pdev = NULL; struct device_node *bitclkprovider = NULL; struct device_node *frameprovider = NULL; - struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; - struct device *codec_dev[2] = { NULL, NULL }; + const struct fsl_asoc_card_pdata *pdata; struct snd_soc_dai_link_component *dlc; - const char *codec_dai_name[2]; - const char *codec_dev_name[2]; + const char *codec_dai_name[2] = { NULL, NULL }; u32 asrc_fmt = 0; int codec_idx; u32 width; @@ -714,6 +1100,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->pdev = pdev; + pdata = of_device_get_match_data(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "unknown Device Tree compatible\n"); + return -EINVAL; + } + priv->pdata = pdata; + cpu_np = of_parse_phandle(np, "audio-cpu", 0); /* Give a chance to old DT bindings */ if (!cpu_np) @@ -726,52 +1119,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto fail; } - cpu_pdev = of_find_device_by_node(cpu_np); - if (!cpu_pdev) { - dev_err(&pdev->dev, "failed to find CPU DAI device\n"); - ret = -EINVAL; - goto fail; - } - codec_np[0] = of_parse_phandle(np, "audio-codec", 0); codec_np[1] = of_parse_phandle(np, "audio-codec", 1); - for (codec_idx = 0; codec_idx < 2; codec_idx++) { - if (codec_np[codec_idx]) { - struct platform_device *codec_pdev; - struct i2c_client *codec_i2c; - - codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]); - if (codec_i2c) { - codec_dev[codec_idx] = &codec_i2c->dev; - codec_dev_name[codec_idx] = codec_i2c->name; - } - if (!codec_dev[codec_idx]) { - codec_pdev = of_find_device_by_node(codec_np[codec_idx]); - if (codec_pdev) { - codec_dev[codec_idx] = &codec_pdev->dev; - codec_dev_name[codec_idx] = codec_pdev->name; - } - } - } - } - asrc_np = of_parse_phandle(np, "audio-asrc", 0); if (asrc_np) asrc_pdev = of_find_device_by_node(asrc_np); - /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ - for (codec_idx = 0; codec_idx < 2; codec_idx++) { - if (codec_dev[codec_idx]) { - struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL); - - if (!IS_ERR(codec_clk)) { - priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk); - clk_put(codec_clk); - } - } - } - /* Default sample rate and format, will be updated in hw_params() */ priv->sample_rate = 44100; priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; @@ -823,133 +1177,40 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } /* Diversify the card configurations */ - if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { - codec_dai_name[0] = "cs42888"; - priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq; - priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq; - priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; - priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; - priv->cpu_priv.slot_width = 32; - priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; - priv->constraint_channels = &cs42888_channel_constraints; - if (priv->codec_priv[0].mclk_freq % 12288000 == 0) - priv->constraint_rates = &cs42888_rate_48k_constraints; - else if (priv->codec_priv[0].mclk_freq % 11289600 == 0) - priv->constraint_rates = &cs42888_rate_44k_constraints; - else - dev_warn(&pdev->dev, "Unknown MCLK frequency %lu, no rate constraints\n", - priv->codec_priv[0].mclk_freq); - } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { - codec_dai_name[0] = "cs4271-hifi"; - priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { - codec_dai_name[0] = "sgtl5000"; - priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { - codec_dai_name[0] = "tlv320aic32x4-hifi"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { - codec_dai_name[0] = "tlv320dac31xx-hifi"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; - priv->dai_link[1].playback_only = 1; - priv->dai_link[2].playback_only = 1; - priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; - priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; - priv->card.dapm_routes = audio_map_tx; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { - codec_dai_name[0] = "wm8962"; - priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK; - priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL; - priv->codec_priv[0].pll_id = WM8962_FLL; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - /* - * WM8962 has same BCLK generation limitations as WM8960. - * See WM8960 section for detailed explanation. - */ - if (of_node_name_eq(cpu_np, "sai")) - priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { - codec_dai_name[0] = "wm8960-hifi"; - priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO; - priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - /* - * WM8960 in master mode cannot generate exact 1.92 MHz BCLK - * required for S20_3LE (48kHz × 2ch × 20bit). Closest available - * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. - * - * In SAI master mode, SAI derive BCLK from MCLK using integer - * dividers only. S20_3LE requires non-integer divider ratios - * with standard MCLK frequencies. For example, 48kHz stereo - * needs 1.920 MHz BCLK, which requires a divider of 6.4 from - * 12.288 MHz MCLK (not an integer). - */ - if (of_node_name_eq(cpu_np, "sai")) - priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; - } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { - codec_dai_name[0] = "ac97-hifi"; - priv->dai_fmt = SND_SOC_DAIFMT_AC97; - priv->card.dapm_routes = audio_map_ac97; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97); - } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { - codec_dai_name[0] = "fsl-mqs-dai"; - priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_CBC_CFC | - SND_SOC_DAIFMT_NB_NF; - priv->dai_link[1].playback_only = 1; - priv->dai_link[2].playback_only = 1; - priv->card.dapm_routes = audio_map_tx; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { - codec_dai_name[0] = "wm8524-hifi"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; + priv->cpu_priv.sysclk_dir[TX] = pdata->sysclk_dir[TX]; + priv->cpu_priv.sysclk_dir[RX] = pdata->sysclk_dir[RX]; + priv->cpu_priv.sysclk_ratio[TX] = pdata->sysclk_ratio[TX]; + priv->cpu_priv.sysclk_ratio[RX] = pdata->sysclk_ratio[RX]; + priv->cpu_priv.slot_width = pdata->slot_width; + + codec_dai_name[0] = pdata->codec_dai_name; + priv->codec_priv[0].mclk_id = pdata->codec_mclk_id; + if (pdata->has_pll) { + priv->codec_priv[0].fll_id = pdata->codec_fll_id; + priv->codec_priv[0].pll_id = pdata->codec_pll_id; + } + if (pdata->codec_pll_ratio_s24) + priv->codec_priv[0].pll_ratio_s24 = pdata->codec_pll_ratio_s24; + + if (pdata->playback_only) { priv->dai_link[1].playback_only = 1; priv->dai_link[2].playback_only = 1; - priv->cpu_priv.slot_width = 32; - priv->card.dapm_routes = audio_map_tx; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); - priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; - priv->cpu_priv.sysclk_ratio[TX] = 256; - } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { - codec_dai_name[0] = "si476x-codec"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; - priv->card.dapm_routes = audio_map_rx; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) { - codec_dai_name[0] = "wm8994-aif1"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1; - priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1; - priv->codec_priv[0].pll_id = WM8994_FLL1; - priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; - priv->card.dapm_routes = NULL; - priv->card.num_dapm_routes = 0; - } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { - codec_dai_name[0] = "nau8822-hifi"; - priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; - priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; - priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - if (codec_dev[0]) - priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { - codec_dai_name[0] = "wm8904-hifi"; - priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; - priv->codec_priv[0].fll_id = WM8904_CLK_FLL; - priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; - priv->codec_priv[0].pll_ratio_s24 = 192; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) { - ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv); + } + if (pdata->capture_only) { + priv->dai_link[1].capture_only = 1; + priv->dai_link[2].capture_only = 1; + } + + priv->dai_fmt = pdata->dai_fmt; + + priv->card.dapm_routes = pdata->dapm_routes; + priv->card.num_dapm_routes = pdata->num_dapm_routes; + + if (pdata->probe_init) { + ret = pdata->probe_init(codec_np, cpu_np, + codec_dai_name, priv); if (ret) goto asrc_fail; - } else { - dev_err(&pdev->dev, "unknown Device Tree compatible\n"); - ret = -EINVAL; - goto asrc_fail; } /* @@ -995,51 +1256,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) of_node_put(bitclkprovider); of_node_put(frameprovider); - if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0] - && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) { - dev_dbg(&pdev->dev, "failed to find codec device\n"); - ret = -EPROBE_DEFER; - goto asrc_fail; - } - - /* Common settings for corresponding Freescale CPU DAI driver */ - if (of_node_name_eq(cpu_np, "ssi")) { - /* Only SSI needs to configure AUDMUX */ - ret = fsl_asoc_card_audmux_init(np, priv); - if (ret) { - dev_err(&pdev->dev, "failed to init audmux\n"); - goto asrc_fail; - } - } else if (of_node_name_eq(cpu_np, "esai")) { - struct clk *esai_clk = clk_get(&cpu_pdev->dev, "extal"); - - if (!IS_ERR(esai_clk)) { - priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); - priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); - clk_put(esai_clk); - } else if (PTR_ERR(esai_clk) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto asrc_fail; - } - - priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; - priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; - } else if (of_node_name_eq(cpu_np, "sai")) { - priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; - priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; - } - /* Initialize sound card */ priv->card.dev = &pdev->dev; priv->card.owner = THIS_MODULE; ret = snd_soc_of_parse_card_name(&priv->card, "model"); if (ret) { - snprintf(priv->name, sizeof(priv->name), "%s-audio", - fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]); - priv->card.name = priv->name; + /* + * "model" is required by the DT binding. Enforce it here so + * the driver fails with a clear message. + */ + dev_err(&pdev->dev, "Error parsing card name: %d\n", ret); + goto asrc_fail; } priv->card.dai_link = priv->dai_link; priv->card.late_probe = fsl_asoc_card_late_probe; + priv->card.remove = fsl_asoc_card_card_remove; priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); @@ -1146,39 +1377,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto asrc_fail; } - /* - * Properties "hp-det-gpios" and "mic-det-gpios" are optional, and - * simple_util_init_jack() uses these properties for creating - * Headphone Jack and Microphone Jack. - * - * The notifier is initialized in snd_soc_card_jack_new(), then - * snd_soc_jack_notifier_register can be called. - */ - if (of_property_present(np, "hp-det-gpios") || - of_property_present(np, "hp-det-gpio") /* deprecated */) { - ret = simple_util_init_jack(&priv->card, &priv->hp_jack, - 1, NULL, "Headphone Jack"); - if (ret) - goto asrc_fail; - - snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); - } - - if (of_property_present(np, "mic-det-gpios") || - of_property_present(np, "mic-det-gpio") /* deprecated */) { - ret = simple_util_init_jack(&priv->card, &priv->mic_jack, - 0, NULL, "Mic Jack"); - if (ret) - goto asrc_fail; - - snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); - } - asrc_fail: of_node_put(asrc_np); of_node_put(codec_np[0]); of_node_put(codec_np[1]); - put_device(&cpu_pdev->dev); fail: of_node_put(cpu_np); @@ -1186,21 +1388,21 @@ fail: } static const struct of_device_id fsl_asoc_card_dt_ids[] = { - { .compatible = "fsl,imx-audio-ac97", }, - { .compatible = "fsl,imx-audio-cs42888", }, - { .compatible = "fsl,imx-audio-cs427x", }, - { .compatible = "fsl,imx-audio-tlv320aic32x4", }, - { .compatible = "fsl,imx-audio-tlv320aic31xx", }, - { .compatible = "fsl,imx-audio-sgtl5000", }, - { .compatible = "fsl,imx-audio-wm8962", }, - { .compatible = "fsl,imx-audio-wm8960", }, - { .compatible = "fsl,imx-audio-mqs", }, - { .compatible = "fsl,imx-audio-wm8524", }, - { .compatible = "fsl,imx-audio-si476x", }, - { .compatible = "fsl,imx-audio-wm8958", }, - { .compatible = "fsl,imx-audio-nau8822", }, - { .compatible = "fsl,imx-audio-wm8904", }, - { .compatible = "fsl,imx-audio-spdif", }, + { .compatible = "fsl,imx-audio-ac97", .data = &fsl_asoc_ac97_pdata }, + { .compatible = "fsl,imx-audio-cs42888", .data = &fsl_asoc_cs42888_pdata }, + { .compatible = "fsl,imx-audio-cs427x", .data = &fsl_asoc_cs427x_pdata }, + { .compatible = "fsl,imx-audio-tlv320aic32x4", .data = &fsl_asoc_tlv320aic32x4_pdata }, + { .compatible = "fsl,imx-audio-tlv320aic31xx", .data = &fsl_asoc_tlv320aic31xx_pdata }, + { .compatible = "fsl,imx-audio-sgtl5000", .data = &fsl_asoc_sgtl5000_pdata }, + { .compatible = "fsl,imx-audio-wm8962", .data = &fsl_asoc_wm8962_pdata }, + { .compatible = "fsl,imx-audio-wm8960", .data = &fsl_asoc_wm8960_pdata }, + { .compatible = "fsl,imx-audio-mqs", .data = &fsl_asoc_mqs_pdata }, + { .compatible = "fsl,imx-audio-wm8524", .data = &fsl_asoc_wm8524_pdata }, + { .compatible = "fsl,imx-audio-si476x", .data = &fsl_asoc_si476x_pdata }, + { .compatible = "fsl,imx-audio-wm8958", .data = &fsl_asoc_wm8958_pdata }, + { .compatible = "fsl,imx-audio-nau8822", .data = &fsl_asoc_nau8822_pdata }, + { .compatible = "fsl,imx-audio-wm8904", .data = &fsl_asoc_wm8904_pdata }, + { .compatible = "fsl,imx-audio-spdif", .data = &fsl_asoc_spdif_pdata }, {} }; MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 11748c65d5cc..b24737add001 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -222,10 +222,9 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) enum asrc_pair_index index = ASRC_INVALID_PAIR; struct fsl_asrc *asrc = pair->asrc; struct device *dev = &asrc->pdev->dev; - unsigned long lock_flags; int i, ret = 0; - spin_lock_irqsave(&asrc->lock, lock_flags); + guard(spinlock_irqsave)(&asrc->lock); for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { if (asrc->pair[i] != NULL) @@ -250,8 +249,6 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) pair->index = index; } - spin_unlock_irqrestore(&asrc->lock, lock_flags); - return ret; } @@ -265,19 +262,16 @@ static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) { struct fsl_asrc *asrc = pair->asrc; enum asrc_pair_index index = pair->index; - unsigned long lock_flags; /* Make sure the pair is disabled */ regmap_update_bits(asrc->regmap, REG_ASRCTR, ASRCTR_ASRCEi_MASK(index), 0); - spin_lock_irqsave(&asrc->lock, lock_flags); + guard(spinlock_irqsave)(&asrc->lock); asrc->channel_avail += pair->channels; asrc->pair[index] = NULL; pair->error = 0; - - spin_unlock_irqrestore(&asrc->lock, lock_flags); } /** diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 7a81366a0ee4..f57e684d1cca 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -444,7 +444,7 @@ struct dma_block { }; /** - * fsl_asrc_soc_data: soc specific data + * struct fsl_asrc_soc_data - soc specific data * * @use_edma: using edma as dma device or not * @channel_bits: width of ASRCNCR register for each pair @@ -457,7 +457,7 @@ struct fsl_asrc_soc_data { }; /** - * fsl_asrc_pair_priv: ASRC Pair private data + * struct fsl_asrc_pair_priv - ASRC Pair private data * * @config: configuration profile */ @@ -466,7 +466,7 @@ struct fsl_asrc_pair_priv { }; /** - * fsl_asrc_priv: ASRC private data + * struct fsl_asrc_priv - ASRC private data * * @asrck_clk: clock sources to driver ASRC internal logic * @soc: soc specific data diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 38f2b7c63133..2f662bdf14d0 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -219,7 +219,9 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, */ component_be = snd_soc_lookup_component_nolocked(dev_be, SND_DMAENGINE_PCM_DRV_NAME); if (component_be) { - be_chan = soc_component_to_pcm(component_be)->chan[substream->stream]; + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component_be); + + be_chan = pcm->chan[substream->stream]; tmp_chan = be_chan; } if (!tmp_chan) { diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index f819f33ec46b..d4140e170edf 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -286,7 +286,6 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai); - unsigned long lock_flags; /* Capture stream shall not be handled */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -296,16 +295,14 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - spin_lock_irqsave(&priv->lock, lock_flags); - priv->tdms |= BIT(dai->driver->id); - spin_unlock_irqrestore(&priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &priv->lock) + priv->tdms |= BIT(dai->driver->id); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - spin_lock_irqsave(&priv->lock, lock_flags); - priv->tdms &= ~BIT(dai->driver->id); - spin_unlock_irqrestore(&priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &priv->lock) + priv->tdms &= ~BIT(dai->driver->id); break; default: return -EINVAL; @@ -457,6 +454,9 @@ static const struct of_device_id fsl_audmix_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_audmix_ids); +static int fsl_audmix_runtime_resume(struct device *dev); +static int fsl_audmix_runtime_suspend(struct device *dev); + static int fsl_audmix_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -488,13 +488,25 @@ static int fsl_audmix_probe(struct platform_device *pdev) spin_lock_init(&priv->lock); platform_set_drvdata(pdev, priv); pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = fsl_audmix_runtime_resume(dev); + if (ret) + goto err_disable_pm; + } + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err_pm_get_sync; + + /* To enable regmap cache only when runtime PM enabled */ + pm_runtime_put(dev); ret = devm_snd_soc_register_component(dev, &fsl_audmix_component, fsl_audmix_dai, ARRAY_SIZE(fsl_audmix_dai)); if (ret) { dev_err(dev, "failed to register ASoC DAI\n"); - goto err_disable_pm; + goto err_pm_get_sync; } /* @@ -506,12 +518,15 @@ static int fsl_audmix_probe(struct platform_device *pdev) if (IS_ERR(priv->pdev)) { ret = PTR_ERR(priv->pdev); dev_err(dev, "failed to register platform: %d\n", ret); - goto err_disable_pm; + goto err_pm_get_sync; } } return 0; +err_pm_get_sync: + if (!pm_runtime_status_suspended(dev)) + fsl_audmix_runtime_suspend(dev); err_disable_pm: pm_runtime_disable(dev); return ret; @@ -522,6 +537,8 @@ static void fsl_audmix_remove(struct platform_device *pdev) struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev); pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + fsl_audmix_runtime_suspend(&pdev->dev); if (priv->pdev) platform_device_unregister(priv->pdev); diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index b12474880185..b1b341132dd6 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -18,8 +18,6 @@ #include <linux/delay.h> #include <linux/gfp.h> #include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/of_platform.h> #include <linux/list.h> #include <linux/slab.h> @@ -824,9 +822,34 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct device_node *ssi_np; struct resource res; + void __iomem *channel; const uint32_t *iprop; + int irq; int ret; + channel = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(channel)) + return PTR_ERR(channel); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + dma = devm_kzalloc(&pdev->dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + dma->dai.name = DRV_NAME; + dma->dai.open = fsl_dma_open; + dma->dai.close = fsl_dma_close; + dma->dai.hw_params = fsl_dma_hw_params; + dma->dai.hw_free = fsl_dma_hw_free; + dma->dai.pointer = fsl_dma_pointer; + dma->dai.pcm_new = fsl_dma_new; + + dma->channel = channel; + dma->irq = irq; + /* Find the SSI node that points to us. */ ssi_np = find_ssi_node(np); if (!ssi_np) { @@ -842,55 +865,19 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) return ret; } - dma = kzalloc_obj(*dma); - if (!dma) { - of_node_put(ssi_np); - return -ENOMEM; - } - - dma->dai.name = DRV_NAME; - dma->dai.open = fsl_dma_open; - dma->dai.close = fsl_dma_close; - dma->dai.hw_params = fsl_dma_hw_params; - dma->dai.hw_free = fsl_dma_hw_free; - dma->dai.pointer = fsl_dma_pointer; - dma->dai.pcm_new = fsl_dma_new; - /* Store the SSI-specific information that we need */ dma->ssi_stx_phys = res.start + REG_SSI_STX0; dma->ssi_srx_phys = res.start + REG_SSI_SRX0; iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); + of_node_put(ssi_np); if (iprop) dma->ssi_fifo_depth = be32_to_cpup(iprop); else /* Older 8610 DTs didn't have the fifo-depth property */ dma->ssi_fifo_depth = 8; - of_node_put(ssi_np); - - ret = devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "could not register platform\n"); - kfree(dma); - return ret; - } - - dma->channel = of_iomap(np, 0); - dma->irq = irq_of_parse_and_map(np, 0); - - dev_set_drvdata(&pdev->dev, dma); - - return 0; -} - -static void fsl_soc_dma_remove(struct platform_device *pdev) -{ - struct dma_object *dma = dev_get_drvdata(&pdev->dev); - - iounmap(dma->channel); - irq_dispose_mapping(dma->irq); - kfree(dma); + return devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); } static const struct of_device_id fsl_soc_dma_ids[] = { @@ -905,7 +892,6 @@ static struct platform_driver fsl_soc_dma_driver = { .of_match_table = fsl_soc_dma_ids, }, .probe = fsl_soc_dma_probe, - .remove = fsl_soc_dma_remove, }; module_platform_driver(fsl_soc_dma_driver); diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h index f19ae765b656..928d91e5236c 100644 --- a/sound/soc/fsl/fsl_dma.h +++ b/sound/soc/fsl/fsl_dma.h @@ -92,7 +92,7 @@ static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x) #define CCSR_DMA_ATR_SNOOP 0x00050000 #define CCSR_DMA_ATR_ESAD_MASK 0x0000000F -/** +/* * List Descriptor for extended chaining mode DMA operations. * * The CLSDAR register points to the first (in a linked-list) List @@ -107,7 +107,7 @@ struct fsl_dma_list_descriptor { u8 res[8]; /* Reserved */ } __attribute__ ((aligned(32), packed)); -/** +/* * Link Descriptor for basic and extended chaining mode DMA operations. * * A Link Descriptor points to a single DMA buffer. Each link descriptor diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 79eb2391058d..aced16d1228a 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1025,7 +1025,6 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) struct fsl_easrc_ctx_priv *ctx_priv; struct fsl_asrc_pair *ctx; struct device *dev; - unsigned long lock_flags; int ret; if (!easrc) @@ -1053,9 +1052,8 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) if (ret) return ret; - spin_lock_irqsave(&easrc->lock, lock_flags); - ret = fsl_easrc_config_slot(easrc, ctx->index); - spin_unlock_irqrestore(&easrc->lock, lock_flags); + scoped_guard(spinlock_irqsave, &easrc->lock) + ret = fsl_easrc_config_slot(easrc, ctx->index); if (ret) return ret; @@ -1301,13 +1299,12 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) enum asrc_pair_index index = ASRC_INVALID_PAIR; struct fsl_asrc *easrc = ctx->asrc; struct device *dev; - unsigned long lock_flags; int ret = 0; int i; dev = &easrc->pdev->dev; - spin_lock_irqsave(&easrc->lock, lock_flags); + guard(spinlock_irqsave)(&easrc->lock); for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) { if (easrc->pair[i]) @@ -1331,8 +1328,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) easrc->channel_avail -= channels; } - spin_unlock_irqrestore(&easrc->lock, lock_flags); - return ret; } @@ -1343,7 +1338,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) */ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) { - unsigned long lock_flags; struct fsl_asrc *easrc; if (!ctx) @@ -1351,14 +1345,12 @@ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) easrc = ctx->asrc; - spin_lock_irqsave(&easrc->lock, lock_flags); + guard(spinlock_irqsave)(&easrc->lock); fsl_easrc_release_slot(easrc, ctx->index); easrc->channel_avail += ctx->channels; easrc->pair[ctx->index] = NULL; - - spin_unlock_irqrestore(&easrc->lock, lock_flags); } /* @@ -1711,12 +1703,12 @@ static const struct reg_default fsl_easrc_reg_defaults[] = { {REG_EASRC_SFS(2), 0x00000000}, {REG_EASRC_SFS(3), 0x00000000}, {REG_EASRC_RRL(0), 0x00000000}, - {REG_EASRC_RRL(1), 0x00000000}, - {REG_EASRC_RRL(2), 0x00000000}, - {REG_EASRC_RRL(3), 0x00000000}, {REG_EASRC_RRH(0), 0x00000000}, + {REG_EASRC_RRL(1), 0x00000000}, {REG_EASRC_RRH(1), 0x00000000}, + {REG_EASRC_RRL(2), 0x00000000}, {REG_EASRC_RRH(2), 0x00000000}, + {REG_EASRC_RRL(3), 0x00000000}, {REG_EASRC_RRH(3), 0x00000000}, {REG_EASRC_RUC(0), 0x00000000}, {REG_EASRC_RUC(1), 0x00000000}, @@ -2058,7 +2050,7 @@ static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buff /* right shift 12 bit to make ratio in 32bit space */ val2 = (u64)in_samples << (frac_bits - 12); val1 = val1 >> 12; - do_div(val2, val1); + val2 = div64_u64(val2, val1); out_samples = val2; out_length = out_samples * out_width * channels; @@ -2292,15 +2284,13 @@ static int fsl_easrc_runtime_suspend(struct device *dev) { struct fsl_asrc *easrc = dev_get_drvdata(dev); struct fsl_easrc_priv *easrc_priv = easrc->private; - unsigned long lock_flags; regcache_cache_only(easrc->regmap, true); clk_disable_unprepare(easrc->mem_clk); - spin_lock_irqsave(&easrc->lock, lock_flags); - easrc_priv->firmware_loaded = 0; - spin_unlock_irqrestore(&easrc->lock, lock_flags); + scoped_guard(spinlock_irqsave, &easrc->lock) + easrc_priv->firmware_loaded = 0; return 0; } @@ -2311,7 +2301,6 @@ static int fsl_easrc_runtime_resume(struct device *dev) struct fsl_easrc_priv *easrc_priv = easrc->private; struct fsl_easrc_ctx_priv *ctx_priv; struct fsl_asrc_pair *ctx; - unsigned long lock_flags; int ret; int i; @@ -2323,13 +2312,11 @@ static int fsl_easrc_runtime_resume(struct device *dev) regcache_mark_dirty(easrc->regmap); regcache_sync(easrc->regmap); - spin_lock_irqsave(&easrc->lock, lock_flags); - if (easrc_priv->firmware_loaded) { - spin_unlock_irqrestore(&easrc->lock, lock_flags); - goto skip_load; + scoped_guard(spinlock_irqsave, &easrc->lock) { + if (easrc_priv->firmware_loaded) + return 0; + easrc_priv->firmware_loaded = 1; } - easrc_priv->firmware_loaded = 1; - spin_unlock_irqrestore(&easrc->lock, lock_flags); ret = fsl_easrc_get_firmware(easrc); if (ret) { @@ -2377,9 +2364,7 @@ static int fsl_easrc_runtime_resume(struct device *dev) goto disable_mem_clk; } -skip_load: return 0; - disable_mem_clk: clk_disable_unprepare(easrc->mem_clk); return ret; diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h index c9f770862662..36569cf8be9a 100644 --- a/sound/soc/fsl/fsl_easrc.h +++ b/sound/soc/fsl/fsl_easrc.h @@ -584,7 +584,7 @@ struct fsl_easrc_slot { }; /** - * fsl_easrc_ctx_priv: EASRC context private data + * struct fsl_easrc_ctx_priv - EASRC context private data * * @in_params: input parameter * @out_params: output parameter @@ -625,7 +625,7 @@ struct fsl_easrc_ctx_priv { }; /** - * fsl_easrc_priv: EASRC private data + * struct fsl_easrc_priv - EASRC private data * * @slot: slot setting * @firmware_hdr: the header of firmware diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index cde0b0c6c1ef..4a530a6c33f0 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -709,10 +709,9 @@ static void fsl_esai_hw_reset(struct work_struct *work) { struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); bool tx = true, rx = false, enabled[2]; - unsigned long lock_flags; u32 tfcr, rfcr; - spin_lock_irqsave(&esai_priv->lock, lock_flags); + guard(spinlock_irqsave)(&esai_priv->lock); /* Save the registers */ regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); @@ -750,8 +749,6 @@ static void fsl_esai_hw_reset(struct work_struct *work) fsl_esai_trigger_start(esai_priv, tx); if (enabled[rx]) fsl_esai_trigger_start(esai_priv, rx); - - spin_unlock_irqrestore(&esai_priv->lock, lock_flags); } static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, @@ -759,7 +756,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - unsigned long lock_flags; esai_priv->channels[tx] = substream->runtime->channels; @@ -767,16 +763,14 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - spin_lock_irqsave(&esai_priv->lock, lock_flags); - fsl_esai_trigger_start(esai_priv, tx); - spin_unlock_irqrestore(&esai_priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &esai_priv->lock) + fsl_esai_trigger_start(esai_priv, tx); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - spin_lock_irqsave(&esai_priv->lock, lock_flags); - fsl_esai_trigger_stop(esai_priv, tx); - spin_unlock_irqrestore(&esai_priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &esai_priv->lock) + fsl_esai_trigger_stop(esai_priv, tx); break; default: return -EINVAL; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 1b9be85b34c2..ad1206ed9882 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -853,17 +853,15 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; - unsigned long flags; int ret = -EAGAIN; - spin_lock_irqsave(&ctrl->ctl_lock, flags); + guard(spinlock_irqsave)(&ctrl->ctl_lock); if (ctrl->ready_buf) { int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; memcpy(&ucontrol->value.iec958.subcode[0], &ctrl->subcode[idx], SPDIF_UBITS_SIZE); ret = 0; } - spin_unlock_irqrestore(&ctrl->ctl_lock, flags); return ret; } @@ -885,17 +883,15 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; - unsigned long flags; int ret = -EAGAIN; - spin_lock_irqsave(&ctrl->ctl_lock, flags); + guard(spinlock_irqsave)(&ctrl->ctl_lock); if (ctrl->ready_buf) { int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; memcpy(&ucontrol->value.bytes.data[0], &ctrl->qsub[idx], SPDIF_QSUB_SIZE); ret = 0; } - spin_unlock_irqrestore(&ctrl->ctl_lock, flags); return ret; } diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index b2e1da1781ae..dc022976c982 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1218,13 +1218,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, if (reg > 0x7f) return; - mutex_lock(&fsl_ac97_data->ac97_reg_lock); + guard(mutex)(&fsl_ac97_data->ac97_reg_lock); ret = clk_prepare_enable(fsl_ac97_data->clk); if (ret) { pr_err("ac97 write clk_prepare_enable failed: %d\n", ret); - goto ret_unlock; + return; } lreg = reg << 12; @@ -1238,9 +1238,6 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, udelay(100); clk_disable_unprepare(fsl_ac97_data->clk); - -ret_unlock: - mutex_unlock(&fsl_ac97_data->ac97_reg_lock); } static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, @@ -1252,12 +1249,12 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, unsigned int lreg; int ret; - mutex_lock(&fsl_ac97_data->ac97_reg_lock); + guard(mutex)(&fsl_ac97_data->ac97_reg_lock); ret = clk_prepare_enable(fsl_ac97_data->clk); if (ret) { pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); - goto ret_unlock; + return val; } lreg = (reg & 0x7f) << 12; @@ -1272,8 +1269,6 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, clk_disable_unprepare(fsl_ac97_data->clk); -ret_unlock: - mutex_unlock(&fsl_ac97_data->ac97_reg_lock); return val; } diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 6677d3bf36ec..982827204351 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -797,10 +797,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, { struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - unsigned long lock_flags; int ret = 0; - spin_lock_irqsave(&xcvr->lock, lock_flags); + guard(spinlock_irqsave)(&xcvr->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -812,7 +811,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_EXT_CTRL_DPTH_RESET(tx)); if (ret < 0) { dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret); - goto release_lock; + return ret; } if (tx) { @@ -824,7 +823,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_ISR_CMDC_TX_EN); if (ret < 0) { dev_err(dai->dev, "err updating isr %d\n", ret); - goto release_lock; + return ret; } fallthrough; case FSL_XCVR_MODE_SPDIF: @@ -833,7 +832,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); if (ret < 0) { dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret); - goto release_lock; + return ret; } break; } @@ -844,14 +843,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0); if (ret < 0) { dev_err(dai->dev, "Failed to enable DMA: %d\n", ret); - goto release_lock; + return ret; } ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); if (ret < 0) { dev_err(dai->dev, "Error while setting IER0: %d\n", ret); - goto release_lock; + return ret; } /* clear DPATH RESET */ @@ -860,7 +859,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, 0); if (ret < 0) { dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret); - goto release_lock; + return ret; } break; @@ -873,14 +872,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_EXT_CTRL_DMA_DIS(tx)); if (ret < 0) { dev_err(dai->dev, "Failed to disable DMA: %d\n", ret); - goto release_lock; + return ret; } ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, FSL_XCVR_IRQ_EARC_ALL, 0); if (ret < 0) { dev_err(dai->dev, "Failed to clear IER0: %d\n", ret); - goto release_lock; + return ret; } if (tx) { @@ -891,7 +890,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); if (ret < 0) { dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); - goto release_lock; + return ret; } if (xcvr->soc_data->spdif_only) break; @@ -905,7 +904,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) { dev_err(dai->dev, "Err updating ISR %d\n", ret); - goto release_lock; + return ret; } break; } @@ -916,18 +915,16 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, break; } -release_lock: - spin_unlock_irqrestore(&xcvr->lock, lock_flags); return ret; } static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) { struct device *dev = &xcvr->pdev->dev; - const struct firmware *fw; int ret = 0, rem, off, out, page = 0, size = FSL_XCVR_REG_OFFSET; u32 mask, val; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, xcvr->soc_data->fw_name, dev); if (ret) { dev_err(dev, "failed to request firmware.\n"); @@ -939,7 +936,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) /* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */ if (rem > 16384) { dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem); - release_firmware(fw); return -ENOMEM; } @@ -950,7 +946,7 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) if (ret < 0) { dev_err(dev, "FW: failed to set page %d, err=%d\n", page, ret); - goto err_firmware; + return ret; } off = page * size; @@ -971,11 +967,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) } } -err_firmware: - release_firmware(fw); - if (ret < 0) - return ret; - /* configure watermarks */ mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK; val = FSL_XCVR_EXT_CTRL_RX_FWM(FSL_XCVR_FIFO_WMK_RX); @@ -1448,11 +1439,10 @@ static void reset_rx_work(struct work_struct *work) { struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst); struct device *dev = &xcvr->pdev->dev; - unsigned long lock_flags; u32 ext_ctrl; dev_dbg(dev, "reset rx path\n"); - spin_lock_irqsave(&xcvr->lock, lock_flags); + guard(spinlock_irqsave)(&xcvr->lock); regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl); if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) { @@ -1469,7 +1459,6 @@ static void reset_rx_work(struct work_struct *work) FSL_XCVR_EXT_CTRL_RX_DPTH_RESET, 0); } - spin_unlock_irqrestore(&xcvr->lock, lock_flags); } static irqreturn_t irq0_isr(int irq, void *devid) diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c index 38aafb8954c7..b55dfbdb4502 100644 --- a/sound/soc/fsl/imx-audio-rpmsg.c +++ b/sound/soc/fsl/imx-audio-rpmsg.c @@ -22,7 +22,6 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data; struct rpmsg_info *info; struct rpmsg_msg *msg; - unsigned long flags; if (!rpmsg->rpmsg_pdev) return 0; @@ -37,21 +36,21 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, /* TYPE C is notification from M core */ switch (r_msg->header.cmd) { case TX_PERIOD_DONE: - spin_lock_irqsave(&info->lock[TX], flags); - msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; - msg->r_msg.param.buffer_tail = - r_msg->param.buffer_tail; - msg->r_msg.param.buffer_tail %= info->num_period[TX]; - spin_unlock_irqrestore(&info->lock[TX], flags); + scoped_guard(spinlock_irqsave, &info->lock[TX]) { + msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; + msg->r_msg.param.buffer_tail = + r_msg->param.buffer_tail; + msg->r_msg.param.buffer_tail %= info->num_period[TX]; + } info->callback[TX](info->callback_param[TX]); break; case RX_PERIOD_DONE: - spin_lock_irqsave(&info->lock[RX], flags); - msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; - msg->r_msg.param.buffer_tail = - r_msg->param.buffer_tail; - msg->r_msg.param.buffer_tail %= info->num_period[1]; - spin_unlock_irqrestore(&info->lock[RX], flags); + scoped_guard(spinlock_irqsave, &info->lock[RX]) { + msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; + msg->r_msg.param.buffer_tail = + r_msg->param.buffer_tail; + msg->r_msg.param.buffer_tail %= info->num_period[1]; + } info->callback[RX](info->callback_param[RX]); break; default: diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 2a4813c6cda9..7086fb7fc1eb 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -39,10 +39,9 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, struct rpmsg_device *rpdev = info->rpdev; int ret = 0; - mutex_lock(&info->msg_lock); + guard(mutex)(&info->msg_lock); if (!rpdev) { dev_err(info->dev, "rpmsg channel not ready\n"); - mutex_unlock(&info->msg_lock); return -EINVAL; } @@ -55,15 +54,12 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, sizeof(struct rpmsg_s_msg)); if (ret) { dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); - mutex_unlock(&info->msg_lock); return ret; } /* No receive msg for TYPE_C command */ - if (msg->s_msg.header.type == MSG_TYPE_C) { - mutex_unlock(&info->msg_lock); + if (msg->s_msg.header.type == MSG_TYPE_C) return 0; - } /* wait response from rpmsg */ ret = wait_for_completion_timeout(&info->cmd_complete, @@ -71,7 +67,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, if (!ret) { dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n", msg->s_msg.header.cmd); - mutex_unlock(&info->msg_lock); return -ETIMEDOUT; } @@ -100,8 +95,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd, info->r_msg.param.resp); - mutex_unlock(&info->msg_lock); - return 0; } @@ -109,14 +102,13 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, struct rpmsg_msg *msg, struct rpmsg_info *info) { - unsigned long flags; int ret = 0; /* * Queue the work to workqueue. * If the queue is full, drop the message. */ - spin_lock_irqsave(&info->wq_lock, flags); + guard(spinlock_irqsave)(&info->wq_lock); if (info->work_write_index != info->work_read_index) { int index = info->work_write_index; @@ -130,7 +122,6 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, info->msg_drop_count[substream->stream]++; ret = -EPIPE; } - spin_unlock_irqrestore(&info->wq_lock, flags); return ret; } @@ -523,7 +514,6 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, snd_pcm_sframes_t avail; struct timer_list *timer; struct rpmsg_msg *msg; - unsigned long flags; int buffer_tail = 0; int written_num; @@ -553,11 +543,11 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, msg->s_msg.param.buffer_tail = buffer_tail; /* The notification message is updated to latest */ - spin_lock_irqsave(&info->lock[substream->stream], flags); - memcpy(&info->notify[substream->stream], msg, - sizeof(struct rpmsg_s_msg)); - info->notify_updated[substream->stream] = true; - spin_unlock_irqrestore(&info->lock[substream->stream], flags); + scoped_guard(spinlock_irqsave, &info->lock[substream->stream]) { + memcpy(&info->notify[substream->stream], msg, + sizeof(struct rpmsg_s_msg)); + info->notify_updated[substream->stream] = true; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) avail = snd_pcm_playback_hw_avail(runtime); @@ -641,7 +631,7 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) bool is_notification = false; struct rpmsg_info *info; struct rpmsg_msg msg; - unsigned long flags; + bool updated; work_of_rpmsg = container_of(work, struct work_of_rpmsg, work); info = work_of_rpmsg->info; @@ -652,25 +642,26 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) * enough data in M core side, need to let M core know * data is updated immediately. */ - spin_lock_irqsave(&info->lock[TX], flags); - if (info->notify_updated[TX]) { - memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); - info->notify_updated[TX] = false; - spin_unlock_irqrestore(&info->lock[TX], flags); - info->send_message(&msg, info); - } else { - spin_unlock_irqrestore(&info->lock[TX], flags); + scoped_guard(spinlock_irqsave, &info->lock[TX]) { + updated = info->notify_updated[TX]; + if (updated) { + memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); + info->notify_updated[TX] = false; + } } - - spin_lock_irqsave(&info->lock[RX], flags); - if (info->notify_updated[RX]) { - memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); - info->notify_updated[RX] = false; - spin_unlock_irqrestore(&info->lock[RX], flags); + if (updated) info->send_message(&msg, info); - } else { - spin_unlock_irqrestore(&info->lock[RX], flags); + + scoped_guard(spinlock_irqsave, &info->lock[RX]) { + updated = info->notify_updated[RX]; + if (updated) { + memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); + info->notify_updated[RX] = false; + } } + if (updated) + info->send_message(&msg, info); + /* Skip the notification message for it has been processed above */ if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C && @@ -682,10 +673,10 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) info->send_message(&work_of_rpmsg->msg, info); /* update read index */ - spin_lock_irqsave(&info->wq_lock, flags); - info->work_read_index++; - info->work_read_index %= WORK_MAX_NUM; - spin_unlock_irqrestore(&info->wq_lock, flags); + scoped_guard(spinlock_irqsave, &info->wq_lock) { + info->work_read_index++; + info->work_read_index %= WORK_MAX_NUM; + } } static int imx_rpmsg_pcm_probe(struct platform_device *pdev) @@ -819,9 +810,9 @@ static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = { }; static const struct platform_device_id imx_rpmsg_pcm_id_table[] = { - { .name = "rpmsg-audio-channel" }, - { .name = "rpmsg-micfil-channel" }, - { }, + { .name = "rpmsg-audio-channel" }, + { .name = "rpmsg-micfil-channel" }, + { } }; MODULE_DEVICE_TABLE(platform, imx_rpmsg_pcm_id_table); diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 56e2cf2f727b..8327fff3e1b5 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -77,18 +77,20 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) { struct psc_dma_stream *s = _psc_dma_stream; - spin_lock(&s->psc_dma->lock); - /* For each finished period, dequeue the completed period buffer - * and enqueue a new one in it's place. */ - while (bcom_buffer_done(s->bcom_task)) { - bcom_retrieve_buffer(s->bcom_task, NULL, NULL); + scoped_guard(spinlock, &s->psc_dma->lock) { + /* + * For each finished period, dequeue the completed period buffer + * and enqueue a new one in its place + */ + while (bcom_buffer_done(s->bcom_task)) { + bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - s->period_current = (s->period_current+1) % s->runtime->periods; - s->period_count++; + s->period_current = (s->period_current+1) % s->runtime->periods; + s->period_count++; - psc_dma_bcom_enqueue_next_buffer(s); + psc_dma_bcom_enqueue_next_buffer(s); + } } - spin_unlock(&s->psc_dma->lock); /* If the stream is active, then also inform the PCM middle layer * of the period finished event. */ @@ -116,7 +118,6 @@ static int psc_dma_trigger(struct snd_soc_component *component, struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; u16 imr; - unsigned long flags; int i; switch (cmd) { @@ -135,19 +136,18 @@ static int psc_dma_trigger(struct snd_soc_component *component, /* Fill up the bestcomm bd queue and enable DMA. * This will begin filling the PSC's fifo. */ - spin_lock_irqsave(&psc_dma->lock, flags); - - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - bcom_gen_bd_rx_reset(s->bcom_task); - else - bcom_gen_bd_tx_reset(s->bcom_task); + scoped_guard(spinlock_irqsave, &psc_dma->lock) { + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + bcom_gen_bd_rx_reset(s->bcom_task); + else + bcom_gen_bd_tx_reset(s->bcom_task); - for (i = 0; i < runtime->periods; i++) - if (!bcom_queue_full(s->bcom_task)) - psc_dma_bcom_enqueue_next_buffer(s); + for (i = 0; i < runtime->periods; i++) + if (!bcom_queue_full(s->bcom_task)) + psc_dma_bcom_enqueue_next_buffer(s); - bcom_enable(s->bcom_task); - spin_unlock_irqrestore(&psc_dma->lock, flags); + bcom_enable(s->bcom_task); + } out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); @@ -158,13 +158,13 @@ static int psc_dma_trigger(struct snd_soc_component *component, substream->pstr->stream, s->period_count); s->active = 0; - spin_lock_irqsave(&psc_dma->lock, flags); - bcom_disable(s->bcom_task); - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - bcom_gen_bd_rx_reset(s->bcom_task); - else - bcom_gen_bd_tx_reset(s->bcom_task); - spin_unlock_irqrestore(&psc_dma->lock, flags); + scoped_guard(spinlock_irqsave, &psc_dma->lock) { + bcom_disable(s->bcom_task); + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + bcom_gen_bd_rx_reset(s->bcom_task); + else + bcom_gen_bd_tx_reset(s->bcom_task); + } break; @@ -314,35 +314,29 @@ int mpc5200_audio_dma_create(struct platform_device *op) { phys_addr_t fifo; struct psc_dma *psc_dma; - struct resource res; + struct resource *res; int size, irq, rc; const __be32 *prop; void __iomem *regs; - int ret; + + regs = devm_platform_get_and_ioremap_resource(op, 0, &res); + if (IS_ERR(regs)) + return PTR_ERR(regs); /* Fetch the registers and IRQ of the PSC */ - irq = irq_of_parse_and_map(op->dev.of_node, 0); - if (of_address_to_resource(op->dev.of_node, 0, &res)) { - dev_err(&op->dev, "Missing reg property\n"); - return -ENODEV; - } - regs = devm_ioremap(&op->dev, res.start, resource_size(&res)); - if (!regs) { - dev_err(&op->dev, "Could not map registers\n"); - return -ENODEV; - } + irq = platform_get_irq(op, 0); + if (irq < 0) + return irq; /* Allocate and initialize the driver private data */ - psc_dma = kzalloc_obj(*psc_dma); + psc_dma = devm_kzalloc(&op->dev, sizeof(*psc_dma), GFP_KERNEL); if (!psc_dma) return -ENOMEM; /* Get the PSC ID */ prop = of_get_property(op->dev.of_node, "cell-index", &size); - if (!prop || size < sizeof *prop) { - ret = -ENODEV; - goto out_free; - } + if (!prop || size < sizeof *prop) + return -ENODEV; spin_lock_init(&psc_dma->lock); mutex_init(&psc_dma->mutex); @@ -357,7 +351,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) /* Find the address of the fifo data registers and setup the * DMA tasks */ - fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); + fifo = res->start + offsetof(struct mpc52xx_psc, buffer.buffer_32); psc_dma->capture.bcom_task = bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); psc_dma->playback.bcom_task = @@ -365,8 +359,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) if (!psc_dma->capture.bcom_task || !psc_dma->playback.bcom_task) { dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); - ret = -ENODEV; - goto out_free; + return -ENODEV; } /* Disable all interrupts and reset the PSC */ @@ -399,16 +392,14 @@ int mpc5200_audio_dma_create(struct platform_device *op) psc_dma->capture.irq = bcom_get_task_irq(psc_dma->capture.bcom_task); - rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, + rc = devm_request_irq(&op->dev, psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, "psc-dma-status", psc_dma); - rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, + rc |= devm_request_irq(&op->dev, psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, "psc-dma-capture", &psc_dma->capture); - rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, + rc |= devm_request_irq(&op->dev, psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, "psc-dma-playback", &psc_dma->playback); - if (rc) { - ret = -ENODEV; - goto out_irq; - } + if (rc) + return -ENODEV; /* Save what we've done so it can be found again later */ dev_set_drvdata(&op->dev, psc_dma); @@ -416,13 +407,6 @@ int mpc5200_audio_dma_create(struct platform_device *op) /* Tell the ASoC OF helpers about it */ return devm_snd_soc_register_component(&op->dev, &mpc5200_audio_dma_component, NULL, 0); -out_irq: - free_irq(psc_dma->irq, psc_dma); - free_irq(psc_dma->capture.irq, &psc_dma->capture); - free_irq(psc_dma->playback.irq, &psc_dma->playback); -out_free: - kfree(psc_dma); - return ret; } EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); @@ -435,12 +419,6 @@ int mpc5200_audio_dma_destroy(struct platform_device *op) bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); - /* Release irqs */ - free_irq(psc_dma->irq, psc_dma); - free_irq(psc_dma->capture.irq, &psc_dma->capture); - free_irq(psc_dma->playback.irq, &psc_dma->playback); - - kfree(psc_dma); dev_set_drvdata(&op->dev, NULL); return 0; diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index c21104355aa0..ccd8bda05860 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -30,14 +30,13 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) int status; unsigned int val; - mutex_lock(&psc_dma->mutex); + guard(mutex)(&psc_dma->mutex); /* Wait for command send status zero = ready */ status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & MPC52xx_PSC_SR_CMDSEND), 100, 0); if (status == 0) { pr_err("timeout on ac97 bus (rdy)\n"); - mutex_unlock(&psc_dma->mutex); return -ENODEV; } @@ -53,19 +52,16 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) if (status == 0) { pr_err("timeout on ac97 read (val) %x\n", in_be16(&psc_dma->psc_regs->sr_csr.status)); - mutex_unlock(&psc_dma->mutex); return -ENODEV; } /* Get the data */ val = in_be32(&psc_dma->psc_regs->ac97_data); if (((val >> 24) & 0x7f) != reg) { pr_err("reg echo error on ac97 read\n"); - mutex_unlock(&psc_dma->mutex); return -ENODEV; } val = (val >> 8) & 0xffff; - mutex_unlock(&psc_dma->mutex); return (unsigned short) val; } @@ -74,52 +70,46 @@ static void psc_ac97_write(struct snd_ac97 *ac97, { int status; - mutex_lock(&psc_dma->mutex); + guard(mutex)(&psc_dma->mutex); /* Wait for command status zero = ready */ status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & MPC52xx_PSC_SR_CMDSEND), 100, 0); if (status == 0) { pr_err("timeout on ac97 bus (write)\n"); - goto out; + return; } /* Write data */ out_be32(&psc_dma->psc_regs->ac97_cmd, ((reg & 0x7f) << 24) | (val << 8)); - - out: - mutex_unlock(&psc_dma->mutex); } static void psc_ac97_warm_reset(struct snd_ac97 *ac97) { struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - mutex_lock(&psc_dma->mutex); + guard(mutex)(&psc_dma->mutex); out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR); udelay(3); out_be32(®s->sicr, psc_dma->sicr); - - mutex_unlock(&psc_dma->mutex); } static void psc_ac97_cold_reset(struct snd_ac97 *ac97) { struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - mutex_lock(&psc_dma->mutex); - dev_dbg(psc_dma->dev, "cold reset\n"); + scoped_guard(mutex, &psc_dma->mutex) { + dev_dbg(psc_dma->dev, "cold reset\n"); - mpc5200_psc_ac97_gpio_reset(psc_dma->id); + mpc5200_psc_ac97_gpio_reset(psc_dma->id); - /* Notify the PSC that a reset has occurred */ - out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); + /* Notify the PSC that a reset has occurred */ + out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); - /* Re-enable RX and TX */ - out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); - - mutex_unlock(&psc_dma->mutex); + /* Re-enable RX and TX */ + out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); + } usleep_range(1000, 2000); psc_ac97_warm_reset(ac97); @@ -286,7 +276,7 @@ static int psc_ac97_of_probe(struct platform_device *op) return rc; } - rc = snd_soc_register_component(&op->dev, &psc_ac97_component, + rc = devm_snd_soc_register_component(&op->dev, &psc_ac97_component, psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); if (rc != 0) { dev_err(&op->dev, "Failed to register DAI\n"); @@ -312,8 +302,6 @@ static int psc_ac97_of_probe(struct platform_device *op) static void psc_ac97_of_remove(struct platform_device *op) { mpc5200_audio_dma_destroy(op); - snd_soc_unregister_component(&op->dev); - snd_soc_set_ac97_ops(NULL); } /* Match table for of_platform binding */ diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 9ad44eeed6ad..c024a38e3b9c 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -79,9 +79,12 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, * and we don't care about the frequency. Return an error if the direction * is not SND_SOC_CLOCK_IN. * + * @cpu_dai: DAI runtime data pointer * @clk_id: reserved, should be zero * @freq: the frequency of the given clock ID, currently ignored * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) + * + * Returns: %0 on success or %-EINVAL on failure. */ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) @@ -101,7 +104,10 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, * This driver only supports I2S mode. Return an error if the format is * not SND_SOC_DAIFMT_I2S. * + * @cpu_dai: DAI runtime data pointer * @format: one of SND_SOC_DAIFMT_xxx + * + * Returns: %0 on success or %-EINVAL on failure. */ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) { @@ -119,7 +125,7 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) */ /** - * psc_i2s_dai_template: template CPU Digital Audio Interface + * var psc_i2s_dai_ops - template CPU Digital Audio Interface */ static const struct snd_soc_dai_ops psc_i2s_dai_ops = { .hw_params = psc_i2s_hw_params, @@ -166,10 +172,11 @@ static int psc_i2s_of_probe(struct platform_device *op) if (rc != 0) return rc; - rc = snd_soc_register_component(&op->dev, &psc_i2s_component, + rc = devm_snd_soc_register_component(&op->dev, &psc_i2s_component, psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); if (rc != 0) { pr_err("Failed to register DAI\n"); + mpc5200_audio_dma_destroy(op); return rc; } @@ -212,7 +219,6 @@ static int psc_i2s_of_probe(struct platform_device *op) static void psc_i2s_of_remove(struct platform_device *op) { mpc5200_audio_dma_destroy(op); - snd_soc_unregister_component(&op->dev); } /* Match table for of_platform binding */ diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index db07817a8024..98389f53246c 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -73,6 +73,8 @@ struct machine_data { char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ }; +#define card_to_mdata(_card) container_of(_card, struct machine_data, card) + /** * p1022_ds_machine_probe: initialize the board * @@ -82,8 +84,7 @@ struct machine_data { */ static int p1022_ds_machine_probe(struct snd_soc_card *card) { - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); struct ccsr_guts __iomem *guts; guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); @@ -122,8 +123,7 @@ static int p1022_ds_machine_probe(struct snd_soc_card *card) static int p1022_ds_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct machine_data *mdata = - container_of(rtd->card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(rtd->card); struct device *dev = rtd->card->dev; int ret = 0; @@ -156,8 +156,7 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) */ static int p1022_ds_machine_remove(struct snd_soc_card *card) { - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); struct ccsr_guts __iomem *guts; guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); @@ -399,8 +398,7 @@ error_put: static void p1022_ds_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); snd_soc_unregister_card(card); kfree(mdata); diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c index f80d6d04e791..d82fe22ca5c5 100644 --- a/sound/soc/fsl/p1022_rdk.c +++ b/sound/soc/fsl/p1022_rdk.c @@ -79,6 +79,8 @@ struct machine_data { char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ }; +#define card_to_mdata(_card) container_of(_card, struct machine_data, card) + /** * p1022_rdk_machine_probe - initialize the board * @card: ASoC card instance @@ -91,8 +93,7 @@ struct machine_data { */ static int p1022_rdk_machine_probe(struct snd_soc_card *card) { - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); struct ccsr_guts __iomem *guts; guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); @@ -134,8 +135,7 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card) static int p1022_rdk_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct machine_data *mdata = - container_of(rtd->card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(rtd->card); struct device *dev = rtd->card->dev; int ret = 0; @@ -169,8 +169,7 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream) */ static int p1022_rdk_machine_remove(struct snd_soc_card *card) { - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); struct ccsr_guts __iomem *guts; guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); @@ -361,8 +360,7 @@ error_put: static void p1022_rdk_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); snd_soc_unregister_card(card); kfree(mdata); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 18ce4ee06350..0a8a6d891d1f 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -579,11 +579,11 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) goto end; } - ret = simple_util_parse_widgets(card, NULL); + ret = simple_util_parse_widgets(priv, NULL); if (ret < 0) goto end; - ret = simple_util_parse_routing(card, NULL); + ret = simple_util_parse_routing(priv, NULL); if (ret < 0) goto end; @@ -594,6 +594,7 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) if (ret < 0) goto err; + /* Card name should be set after graph_for_each_link() */ ret = simple_util_parse_card_name(priv, NULL); if (ret < 0) goto err; @@ -603,14 +604,13 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) simple_util_debug_info(priv); ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - return 0; err: - simple_util_clean_reference(card); + if (ret < 0) { + simple_util_clean_reference(priv); + return dev_err_probe(dev, ret, "parse error\n"); + } end: - return dev_err_probe(dev, ret, "parse error\n"); + return graph_ret(priv, ret); } EXPORT_SYMBOL_GPL(audio_graph_parse_of); diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 0202ed0ee78e..9fb3d3df5cf6 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1303,11 +1303,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, struct graph2_custom_hooks *hooks) { struct snd_soc_card *card = simple_priv_to_card(priv); - int ret; + int ret = -ENOMEM; struct link_info *li __free(kfree) = kzalloc_obj(*li); if (!li) - return -ENOMEM; + goto end; card->probe = graph_util_card_probe; card->owner = THIS_MODULE; @@ -1316,31 +1316,35 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, if ((hooks) && (hooks)->hook_pre) { ret = (hooks)->hook_pre(priv); if (ret < 0) - goto err; + goto end; } ret = graph_for_each_link(priv, hooks, li, graph_count); if (!li->link) ret = -EINVAL; if (ret < 0) - goto err; + goto end; ret = simple_util_init_priv(priv, li); if (ret < 0) - goto err; + goto end; priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); if (IS_ERR(priv->pa_gpio)) { ret = PTR_ERR(priv->pa_gpio); dev_err(dev, "failed to get amplifier gpio: %d\n", ret); - goto err; + goto end; } - ret = simple_util_parse_widgets(card, NULL); + ret = simple_util_parse_widgets(priv, NULL); if (ret < 0) - goto err; + goto end; + + ret = simple_util_parse_routing(priv, NULL); + if (ret < 0) + goto end; - ret = simple_util_parse_routing(card, NULL); + ret = simple_util_parse_aux_devs(priv, NULL); if (ret < 0) goto err; @@ -1349,6 +1353,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, if (ret < 0) goto err; + /* Card name should be set after graph_for_each_link() */ ret = simple_util_parse_card_name(priv, NULL); if (ret < 0) goto err; @@ -1363,15 +1368,13 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, simple_util_debug_info(priv); - ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); - if (ret < 0) - goto err; - ret = devm_snd_soc_register_card(dev, card); err: - if (ret < 0) - dev_err_probe(dev, ret, "parse error\n"); - + if (ret < 0) { + simple_util_clean_reference(priv); + return dev_err_probe(dev, ret, "parse error\n"); + } +end: return graph_ret(priv, 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 e5cb602fd248..42019daa5e04 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -216,6 +216,27 @@ int simple_util_set_dailink_name(struct simple_util_priv *priv, } EXPORT_SYMBOL_GPL(simple_util_set_dailink_name); +int simple_util_parse_property(struct simple_util_priv *priv, + int (*func)(struct snd_soc_card *card, const char *propname), + char *prefix, char *property) +{ + struct snd_soc_card *card = simple_priv_to_card(priv); + struct device_node *node = card->dev->of_node; + char prop[128]; + + if (!prefix) + prefix = ""; + + snprintf(prop, sizeof(prop), "%s%s", prefix, property); + + /* no property is not error */ + if (!of_property_present(node, prop)) + return 0; + + return func(card, prop); +} +EXPORT_SYMBOL_GPL(simple_util_parse_property); + int simple_util_parse_card_name(struct simple_util_priv *priv, char *prefix) { @@ -731,11 +752,12 @@ void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, } EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu); -void simple_util_clean_reference(struct snd_soc_card *card) +void simple_util_clean_reference(struct simple_util_priv *priv) { struct snd_soc_dai_link *dai_link; struct snd_soc_dai_link_component *cpu; struct snd_soc_dai_link_component *codec; + struct snd_soc_card *card = simple_priv_to_card(priv); int i, j; for_each_card_prelinks(card, i, dai_link) { @@ -747,57 +769,6 @@ void simple_util_clean_reference(struct snd_soc_card *card) } EXPORT_SYMBOL_GPL(simple_util_clean_reference); -int simple_util_parse_routing(struct snd_soc_card *card, - char *prefix) -{ - struct device_node *node = card->dev->of_node; - char prop[128]; - - if (!prefix) - prefix = ""; - - snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); - - if (!of_property_present(node, prop)) - return 0; - - return snd_soc_of_parse_audio_routing(card, prop); -} -EXPORT_SYMBOL_GPL(simple_util_parse_routing); - -int simple_util_parse_widgets(struct snd_soc_card *card, - char *prefix) -{ - struct device_node *node = card->dev->of_node; - char prop[128]; - - if (!prefix) - prefix = ""; - - snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); - - if (of_property_present(node, prop)) - return snd_soc_of_parse_audio_simple_widgets(card, prop); - - /* no widgets is not error */ - return 0; -} -EXPORT_SYMBOL_GPL(simple_util_parse_widgets); - -int simple_util_parse_pin_switches(struct snd_soc_card *card, - char *prefix) -{ - char prop[128]; - - if (!prefix) - prefix = ""; - - snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); - - return snd_soc_of_parse_pin_switches(card, prop); -} -EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches); - int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, int is_hp, char *prefix, @@ -854,9 +825,9 @@ int simple_util_init_jack(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(simple_util_init_jack); -int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) +int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix) { - struct snd_soc_card *card = simple_priv_to_card(priv); + struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); struct snd_soc_component *component; int found_jack_index = 0; int type = 0; @@ -1026,8 +997,9 @@ EXPORT_SYMBOL_GPL(simple_util_init_priv); void simple_util_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); + struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); - simple_util_clean_reference(card); + simple_util_clean_reference(priv); } EXPORT_SYMBOL_GPL(simple_util_remove); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index b4957e025211..e7ad7af04714 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -517,44 +517,6 @@ end: return simple_ret(priv, ret); } -static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) -{ - struct snd_soc_card *card = simple_priv_to_card(priv); - int ret; - - ret = simple_util_parse_widgets(card, PREFIX); - if (ret < 0) - goto end; - - ret = simple_util_parse_routing(card, PREFIX); - if (ret < 0) - goto end; - - ret = simple_util_parse_pin_switches(card, PREFIX); - if (ret < 0) - goto end; - - /* Single/Muti DAI link(s) & New style of DT node */ - memset(li, 0, sizeof(*li)); - ret = simple_for_each_link(priv, li, - simple_dai_link_of, - simple_dai_link_of_dpcm); - if (ret < 0) - goto end; - - ret = simple_util_parse_card_name(priv, PREFIX); - if (ret < 0) - goto end; - - ret = simple_populate_aux(priv); - if (ret < 0) - goto end; - - ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); -end: - return simple_ret(priv, ret); -} - static int simple_count_noml(struct simple_util_priv *priv, struct device_node *np, struct device_node *codec, @@ -699,28 +661,19 @@ static int simple_soc_probe(struct snd_soc_card *card) if (ret < 0) goto end; - ret = simple_util_init_aux_jacks(priv, PREFIX); + ret = simple_util_init_aux_jacks(card, PREFIX); end: return simple_ret(priv, ret); } -static int simple_probe(struct platform_device *pdev) +static int simple_parse_of(struct simple_util_priv *priv) { - struct simple_util_priv *priv; - struct device *dev = &pdev->dev; - struct snd_soc_card *card; - int ret; - - /* Allocate the private data and the DAI link array */ - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + struct snd_soc_card *card = simple_priv_to_card(priv); + struct device *dev = card->dev; + int ret = -EINVAL; - card = simple_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; - card->probe = simple_soc_probe; - card->driver_name = "simple-card"; + if (!dev) + return simple_ret(priv, ret); ret = -ENOMEM; struct link_info *li __free(kfree) = kzalloc_obj(*li); @@ -739,25 +692,71 @@ static int simple_probe(struct platform_device *pdev) if (ret < 0) goto end; - ret = simple_parse_of(priv, li); - if (ret < 0) { - dev_err_probe(dev, ret, "parse error\n"); + ret = simple_util_parse_widgets(priv, PREFIX); + if (ret < 0) + goto end; + + ret = simple_util_parse_routing(priv, PREFIX); + if (ret < 0) + goto end; + + ret = simple_util_parse_pin_switches(priv, PREFIX); + if (ret < 0) + goto end; + + ret = simple_util_parse_aux_devs(priv, PREFIX); + if (ret < 0) goto err; - } - snd_soc_card_set_drvdata(card, priv); + /* Single/Muti DAI link(s) & New style of DT node */ + memset(li, 0, sizeof(*li)); + ret = simple_for_each_link(priv, li, + simple_dai_link_of, + simple_dai_link_of_dpcm); + if (ret < 0) + goto err; - simple_util_debug_info(priv); + /* Card name should be set after simple_for_each_link() */ + ret = simple_util_parse_card_name(priv, PREFIX); + if (ret < 0) + goto err; - ret = devm_snd_soc_register_card(dev, card); + ret = simple_populate_aux(priv); if (ret < 0) goto err; - return 0; + snd_soc_card_set_drvdata(card, priv); + + simple_util_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); err: - simple_util_clean_reference(card); + if (ret < 0) { + simple_util_clean_reference(priv); + return dev_err_probe(dev, ret, "parse error\n"); + } end: - return dev_err_probe(dev, ret, "parse error\n"); + return simple_ret(priv, ret); +} + +static int simple_probe(struct platform_device *pdev) +{ + struct simple_util_priv *priv; + struct device *dev = &pdev->dev; + struct snd_soc_card *card; + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + card = simple_priv_to_card(priv); + card->owner = THIS_MODULE; + card->dev = dev; + card->probe = simple_soc_probe; + card->driver_name = "simple-card"; + + return simple_parse_of(priv); } static const struct of_device_id simple_of_match[] = { diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 3629ceaaac17..82df398237da 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -14,6 +14,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/cleanup.h> #include <linux/slab.h> #include <sound/soc.h> #include <sound/tlv.h> @@ -73,14 +74,10 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, void *cmd_data, u16 len) { - int ret; + guard(mutex)(&drv->lock); - mutex_lock(&drv->lock); - ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, - task_id, pipe_id, cmd_data, len); - mutex_unlock(&drv->lock); - - return ret; + return sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, + task_id, pipe_id, cmd_data, len); } /* @@ -167,7 +164,7 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, unsigned int val, mux; u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); val = 1 << ctl_no; /* search which slot/channel has this bit set - there should be only one */ for (mux = e->max; mux > 0; mux--) @@ -175,7 +172,6 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, break; ucontrol->value.enumerated.item[0] = mux; - mutex_unlock(&drv->lock); dev_dbg(c->dev, "%s - %s map = %#x\n", is_tx ? "tx channel" : "rx slot", @@ -235,7 +231,7 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, if (mux > e->max - 1) return -EINVAL; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); /* first clear all registers of this bit */ for (i = 0; i < e->max; i++) map[i] &= ~val; @@ -244,7 +240,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, /* kctl set to 'none' and we reset the bits so send IPC */ ret = sst_check_and_send_slot_map(drv, kcontrol); - mutex_unlock(&drv->lock); return ret; } @@ -258,7 +253,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, ret = sst_check_and_send_slot_map(drv, kcontrol); - mutex_unlock(&drv->lock); return ret; } @@ -354,13 +348,12 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, struct sst_algo_control *bc = (void *)kcontrol->private_value; dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name); - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); switch (bc->type) { case SST_ALGO_PARAMS: memcpy(bc->params, ucontrol->value.bytes.data, bc->max); break; default: - mutex_unlock(&drv->lock); dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n", bc->type); return -EINVAL; @@ -368,7 +361,6 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, /*if pipe is enabled, need to send the algo params from here*/ if (bc->w && bc->w->power) ret = sst_send_algo_cmd(drv, bc); - mutex_unlock(&drv->lock); return ret; } @@ -475,7 +467,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; struct sst_gain_value *gv = mc->gain_val; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); switch (mc->type) { case SST_GAIN_TLV: @@ -497,7 +489,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, break; default: - mutex_unlock(&drv->lock); dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n", mc->type); return -EINVAL; @@ -506,7 +497,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, if (mc->w && mc->w->power) ret = sst_send_gain_cmd(drv, gv, mc->task_id, mc->pipe_id | mc->instance_id, mc->module_id, 0); - mutex_unlock(&drv->lock); return ret; } @@ -521,10 +511,9 @@ static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, struct sst_data *drv = snd_soc_component_get_drvdata(c); struct sst_ids *ids = w->priv; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); sst_find_and_send_pipe_algo(drv, w->name, ids); sst_set_pipe_gain(ids, drv, 0); - mutex_unlock(&drv->lock); return 0; } @@ -761,27 +750,29 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) return ret; } - mutex_lock(&drv->lock); - if (enable) - timer_usage++; - else - timer_usage--; - - /* - * Send the command only if this call is the first enable or last - * disable - */ - if ((enable && (timer_usage == 1)) || - (!enable && (timer_usage == 0))) { - ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, - SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, - sizeof(cmd.header) + cmd.header.length); - if (ret && enable) { + scoped_guard(mutex, &drv->lock) { + if (enable) + timer_usage++; + else timer_usage--; - enable = false; + + /* + * Send the command only if this call is the first enable or last + * disable + */ + if ((enable && timer_usage == 1) || + (!enable && timer_usage == 0)) { + ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, + SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + + cmd.header.length); + if (ret && enable) { + timer_usage--; + enable = false; + } } } - mutex_unlock(&drv->lock); if (!enable) sst->ops->power(sst->dev, false); diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index f074af2499c8..a4c8cbfba096 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -11,6 +11,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/cleanup.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/module.h> @@ -32,7 +33,7 @@ int sst_register_dsp(struct sst_device *dev) return -EINVAL; if (!try_module_get(dev->dev->driver->owner)) return -ENODEV; - mutex_lock(&sst_lock); + guard(mutex)(&sst_lock); if (sst) { dev_err(dev->dev, "we already have a device %s\n", sst->name); module_put(dev->dev->driver->owner); @@ -41,7 +42,7 @@ int sst_register_dsp(struct sst_device *dev) } dev_dbg(dev->dev, "registering device %s\n", dev->name); sst = dev; - mutex_unlock(&sst_lock); + return 0; } EXPORT_SYMBOL_GPL(sst_register_dsp); @@ -53,17 +54,15 @@ int sst_unregister_dsp(struct sst_device *dev) if (dev != sst) return -EINVAL; - mutex_lock(&sst_lock); + guard(mutex)(&sst_lock); - if (!sst) { - mutex_unlock(&sst_lock); + if (!sst) return -EIO; - } module_put(sst->dev->driver->owner); dev_dbg(dev->dev, "unreg %s\n", sst->name); sst = NULL; - mutex_unlock(&sst_lock); + return 0; } EXPORT_SYMBOL_GPL(sst_unregister_dsp); @@ -103,21 +102,14 @@ static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) void sst_set_stream_status(struct sst_runtime_stream *stream, int state) { - unsigned long flags; - spin_lock_irqsave(&stream->status_lock, flags); + guard(spinlock_irqsave)(&stream->status_lock); stream->stream_status = state; - spin_unlock_irqrestore(&stream->status_lock, flags); } static inline int sst_get_stream_status(struct sst_runtime_stream *stream) { - int state; - unsigned long flags; - - spin_lock_irqsave(&stream->status_lock, flags); - state = stream->stream_status; - spin_unlock_irqrestore(&stream->status_lock, flags); - return state; + guard(spinlock_irqsave)(&stream->status_lock); + return stream->stream_status; } static void sst_fill_alloc_params(struct snd_pcm_substream *substream, @@ -244,12 +236,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, stream->stream_info.str_id = str_params.stream_id; - ret_val = stream->ops->open(sst->dev, &str_params); - if (ret_val <= 0) - return ret_val; - - - return ret_val; + return stream->ops->open(sst->dev, &str_params); } static void sst_period_elapsed(void *arg) @@ -304,7 +291,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, { int ret_val = 0; struct snd_pcm_runtime *runtime = substream->runtime; - struct sst_runtime_stream *stream; + struct sst_runtime_stream *stream __free(kfree) = NULL; stream = kzalloc_obj(*stream); if (!stream) @@ -312,15 +299,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, spin_lock_init(&stream->status_lock); /* get the sst ops */ - mutex_lock(&sst_lock); - if (!sst || - !try_module_get(sst->dev->driver->owner)) { - dev_err(dai->dev, "no device available to run\n"); - ret_val = -ENODEV; - goto out_ops; + scoped_guard(mutex, &sst_lock) { + if (!sst || + !try_module_get(sst->dev->driver->owner)) { + dev_err(dai->dev, "no device available to run\n"); + return -ENODEV; + } + stream->ops = sst->ops; } - stream->ops = sst->ops; - mutex_unlock(&sst_lock); stream->stream_info.str_id = 0; @@ -330,7 +316,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, ret_val = power_up_sst(stream); if (ret_val < 0) - goto out_power_up; + return ret_val; /* * Make sure the period to be multiple of 1ms to align the @@ -347,12 +333,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, 2); - return snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); -out_ops: - mutex_unlock(&sst_lock); -out_power_up: - kfree(stream); + ret_val = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + + if (ret_val < 0) + return ret_val; + + stream = NULL; + return ret_val; } diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 0d5e71e8a5b5..6c19ab63aa4f 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -11,6 +11,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include <linux/cleanup.h> #include <linux/pci.h> #include <linux/firmware.h> #include <linux/sched.h> @@ -180,9 +181,8 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) union interrupt_reg_mrfld isr; union interrupt_reg_mrfld imr; union ipc_header_mrfld clear_ipc; - unsigned long irq_flags; - spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + guard(spinlock_irqsave)(&sst_drv_ctx->ipc_spin_lock); imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); @@ -200,7 +200,6 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) /* un mask busy interrupt */ imr.part.busy_interrupt = 0; sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); - spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); } diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index 22ae2d22f121..44bb11c69490 100644 --- a/sound/soc/intel/atom/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c @@ -130,13 +130,15 @@ static int intel_sst_probe(struct pci_dev *pci, sst_drv_ctx->pci = pci_dev_get(pci); ret = sst_platform_get_resources(sst_drv_ctx); if (ret < 0) - goto do_free_drv_ctx; + goto do_put_pci; pci_set_drvdata(pci, sst_drv_ctx); sst_configure_runtime_pm(sst_drv_ctx); return ret; +do_put_pci: + pci_dev_put(sst_drv_ctx->pci); do_free_drv_ctx: sst_context_cleanup(sst_drv_ctx); dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index 67b1ab14239f..0b0cfd70efbc 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -11,6 +11,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include <linux/cleanup.h> #include <linux/kobject.h> #include <linux/pci.h> #include <linux/fs.h> @@ -64,9 +65,8 @@ u64 sst_shim_read64(void __iomem *addr, int offset) void sst_set_fw_state_locked( struct intel_sst_drv *sst_drv_ctx, int sst_state) { - mutex_lock(&sst_drv_ctx->sst_lock); + guard(mutex)(&sst_drv_ctx->sst_lock); sst_drv_ctx->sst_state = sst_state; - mutex_unlock(&sst_drv_ctx->sst_lock); } /* @@ -302,18 +302,17 @@ int sst_assign_pvt_id(struct intel_sst_drv *drv) { int local; - spin_lock(&drv->block_lock); + guard(spinlock)(&drv->block_lock); /* find first zero index from lsb */ local = ffz(drv->pvt_id); dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local); if (local >= SST_MAX_BLOCKS){ - spin_unlock(&drv->block_lock); dev_err(drv->dev, "PVT _ID error: no free id blocks "); return -EINVAL; } /* toggle the index */ change_bit(local, &drv->pvt_id); - spin_unlock(&drv->block_lock); + return local; } diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index b922eeaba843..cf600e1c986e 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/cleanup.h> #include <linux/devcoredump.h> #include <linux/slab.h> #include <sound/hdaudio_ext.h> @@ -190,7 +191,7 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) { struct avs_path *path; - spin_lock(&adev->path_list_lock); + guard(spinlock)(&adev->path_list_lock); /* Any gateway without buffer allocated in LP area disqualifies D0IX. */ list_for_each_entry(path, &adev->path_list, node) { struct avs_path_pipeline *ppl; @@ -210,14 +211,11 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) if (cfg->copier.dma_type == INVALID_OBJECT_ID) continue; - if (!mod->gtw_attrs.lp_buffer_alloc) { - spin_unlock(&adev->path_list_lock); + if (!mod->gtw_attrs.lp_buffer_alloc) return false; - } } } } - spin_unlock(&adev->path_list_lock); return true; } diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 0f8ddd0e9e5f..b4f9d66d55ad 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -336,14 +336,13 @@ int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw); /* Soc component members */ struct avs_soc_component { - struct snd_soc_component base; + 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) +#define to_avs_soc_component(comp) snd_soc_component_to_priv(comp) extern const struct snd_soc_dai_ops avs_dai_fe_ops; diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 2b17abcbd2bc..b7bdd8a690cf 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -175,7 +175,6 @@ static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot, if (!dl || !platform) return -ENOMEM; - dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); dl->name = devm_kasprintf(dev, GFP_KERNEL, AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); @@ -259,10 +258,8 @@ static int avs_da7219_probe(struct platform_device *pdev) } static const struct platform_device_id avs_da7219_driver_ids[] = { - { - .name = "avs_da7219", - }, - {}, + { .name = "avs_da7219" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids); diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c index bf6f580a5164..8d36bc7ddf16 100644 --- a/sound/soc/intel/avs/boards/dmic.c +++ b/sound/soc/intel/avs/boards/dmic.c @@ -104,10 +104,8 @@ static int avs_dmic_probe(struct platform_device *pdev) } static const struct platform_device_id avs_dmic_driver_ids[] = { - { - .name = "avs_dmic", - }, - {}, + { .name = "avs_dmic" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids); diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c index 301cfb3cf15b..36c13db3a272 100644 --- a/sound/soc/intel/avs/boards/es8336.c +++ b/sound/soc/intel/avs/boards/es8336.c @@ -309,10 +309,8 @@ static int avs_es8336_probe(struct platform_device *pdev) } static const struct platform_device_id avs_es8336_driver_ids[] = { - { - .name = "avs_es8336", - }, - {}, + { .name = "avs_es8336" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids); diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c index aec769e2396c..03cfd91202d3 100644 --- a/sound/soc/intel/avs/boards/hdaudio.c +++ b/sound/soc/intel/avs/boards/hdaudio.c @@ -231,10 +231,8 @@ static int avs_hdaudio_probe(struct platform_device *pdev) } static const struct platform_device_id avs_hdaudio_driver_ids[] = { - { - .name = "avs_hdaudio", - }, - {}, + { .name = "avs_hdaudio" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids); diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c index 9a6b89ffdf14..787d781ba1d9 100644 --- a/sound/soc/intel/avs/boards/i2s_test.c +++ b/sound/soc/intel/avs/boards/i2s_test.c @@ -107,10 +107,8 @@ static int avs_i2s_test_probe(struct platform_device *pdev) } static const struct platform_device_id avs_i2s_test_driver_ids[] = { - { - .name = "avs_i2s_test", - }, - {}, + { .name = "avs_i2s_test" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids); diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c index e9a87804f918..389a50923d3b 100644 --- a/sound/soc/intel/avs/boards/max98357a.c +++ b/sound/soc/intel/avs/boards/max98357a.c @@ -136,10 +136,8 @@ static int avs_max98357a_probe(struct platform_device *pdev) } static const struct platform_device_id avs_max98357a_driver_ids[] = { - { - .name = "avs_max98357a", - }, - {}, + { .name = "avs_max98357a" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids); diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c index 8b45b643ca29..b8231f71d3d6 100644 --- a/sound/soc/intel/avs/boards/max98373.c +++ b/sound/soc/intel/avs/boards/max98373.c @@ -191,10 +191,8 @@ static int avs_max98373_probe(struct platform_device *pdev) } static const struct platform_device_id avs_max98373_driver_ids[] = { - { - .name = "avs_max98373", - }, - {}, + { .name = "avs_max98373" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids); diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c index db073125fa4d..d657e7da1cc0 100644 --- a/sound/soc/intel/avs/boards/max98927.c +++ b/sound/soc/intel/avs/boards/max98927.c @@ -188,10 +188,8 @@ static int avs_max98927_probe(struct platform_device *pdev) } static const struct platform_device_id avs_max98927_driver_ids[] = { - { - .name = "avs_max98927", - }, - {}, + { .name = "avs_max98927" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids); diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index d44edacbfc9a..d7ea08bb27cd 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -293,10 +293,8 @@ static int avs_nau8825_probe(struct platform_device *pdev) } static const struct platform_device_id avs_nau8825_driver_ids[] = { - { - .name = "avs_nau8825", - }, - {}, + { .name = "avs_nau8825" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids); diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c index b5bebadbbcb2..9d415fd0499a 100644 --- a/sound/soc/intel/avs/boards/pcm3168a.c +++ b/sound/soc/intel/avs/boards/pcm3168a.c @@ -132,10 +132,8 @@ static int avs_pcm3168a_probe(struct platform_device *pdev) } static const struct platform_device_id avs_pcm3168a_driver_ids[] = { - { - .name = "avs_pcm3168a", - }, - {}, + { .name = "avs_pcm3168a" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids); diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c index 73884f8a535c..4053d14289b9 100644 --- a/sound/soc/intel/avs/boards/probe.c +++ b/sound/soc/intel/avs/boards/probe.c @@ -64,7 +64,7 @@ static const struct platform_device_id avs_probe_mb_driver_ids[] = { { .name = "avs_probe_mb", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c index a689f4c80867..289f1230851f 100644 --- a/sound/soc/intel/avs/boards/rt274.c +++ b/sound/soc/intel/avs/boards/rt274.c @@ -261,7 +261,7 @@ static const struct platform_device_id avs_rt274_driver_ids[] = { { .name = "avs_rt274", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c index 4c9ac545555a..9364d60cfcc7 100644 --- a/sound/soc/intel/avs/boards/rt286.c +++ b/sound/soc/intel/avs/boards/rt286.c @@ -231,7 +231,7 @@ static const struct platform_device_id avs_rt286_driver_ids[] = { { .name = "avs_rt286", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index 2d7a7748d577..5d3b3cdc9564 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -250,7 +250,7 @@ static const struct platform_device_id avs_rt298_driver_ids[] = { { .name = "avs_rt298", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c index 22139eaad83a..e448bc9fa5ad 100644 --- a/sound/soc/intel/avs/boards/rt5514.c +++ b/sound/soc/intel/avs/boards/rt5514.c @@ -178,7 +178,7 @@ static const struct platform_device_id avs_rt5514_driver_ids[] = { { .name = "avs_rt5514", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c index 2990d32f2301..c4e33d71e82f 100644 --- a/sound/soc/intel/avs/boards/rt5640.c +++ b/sound/soc/intel/avs/boards/rt5640.c @@ -252,7 +252,7 @@ static const struct platform_device_id avs_rt5640_driver_ids[] = { { .name = "avs_rt5640", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c index 68fea325376a..aadbd3f2a1b2 100644 --- a/sound/soc/intel/avs/boards/rt5663.c +++ b/sound/soc/intel/avs/boards/rt5663.c @@ -249,7 +249,7 @@ static const struct platform_device_id avs_rt5663_driver_ids[] = { { .name = "avs_rt5663", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c index 81863728da1d..d33699cca595 100644 --- a/sound/soc/intel/avs/boards/rt5682.c +++ b/sound/soc/intel/avs/boards/rt5682.c @@ -325,7 +325,7 @@ static const struct platform_device_id avs_rt5682_driver_ids[] = { { .name = "avs_rt5682", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids); diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c index ae0e6e27a8b8..d7c9bff37556 100644 --- a/sound/soc/intel/avs/boards/ssm4567.c +++ b/sound/soc/intel/avs/boards/ssm4567.c @@ -180,7 +180,7 @@ static const struct platform_device_id avs_ssm4567_driver_ids[] = { { .name = "avs_ssm4567", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids); diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c index a8f05de338e0..370069247a7d 100644 --- a/sound/soc/intel/avs/control.c +++ b/sound/soc/intel/avs/control.c @@ -27,7 +27,7 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i struct avs_path_pipeline *ppl; struct avs_path_module *mod; - spin_lock(&adev->path_list_lock); + guard(spinlock)(&adev->path_list_lock); list_for_each_entry(path, &adev->path_list, node) { list_for_each_entry(ppl, &path->ppl_list, node) { list_for_each_entry(mod, &ppl->mod_list, node) { @@ -35,14 +35,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || guid_equal(type, &AVS_GAIN_MOD_UUID)) && - mod->template->ctl_id == id) { - spin_unlock(&adev->path_list_lock); + mod->template->ctl_id == id) return mod; - } } } } - spin_unlock(&adev->path_list_lock); return NULL; } diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 1a53856c2ffb..2afe59646896 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -14,6 +14,7 @@ // foundation of this driver // +#include <linux/cleanup.h> #include <linux/acpi.h> #include <linux/module.h> #include <linux/pci.h> @@ -273,7 +274,7 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream)) ret = IRQ_HANDLED; - spin_lock_irq(&bus->reg_lock); + guard(spinlock_irq)(&bus->reg_lock); /* Clear RIRB interrupt. */ status = snd_hdac_chip_readb(bus, RIRBSTS); if (status & RIRB_INT_MASK) { @@ -283,7 +284,6 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) ret = IRQ_HANDLED; } - spin_unlock_irq(&bus->reg_lock); return ret; } diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h index 94fe8729a5c1..c47fc4e8b02b 100644 --- a/sound/soc/intel/avs/debug.h +++ b/sound/soc/intel/avs/debug.h @@ -9,6 +9,7 @@ #ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H #define __SOUND_SOC_INTEL_AVS_DEBUG_H +#include <linux/cleanup.h> #include "messages.h" #include "registers.h" @@ -26,14 +27,9 @@ struct avs_dev; static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg) { - unsigned long flags; - int ret; + guard(spinlock_irqsave)(&adev->trace_lock); - spin_lock_irqsave(&adev->trace_lock, flags); - ret = avs_dsp_op(adev, log_buffer_status, msg); - spin_unlock_irqrestore(&adev->trace_lock, flags); - - return ret; + return avs_dsp_op(adev, log_buffer_status, msg); } struct avs_apl_log_buffer_layout { diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index 701c247227bf..9ab503da3b75 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/kfifo.h> #include <linux/wait.h> @@ -251,24 +252,22 @@ static int strace_release(struct inode *inode, struct file *file) union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); struct avs_dev *adev = file->private_data; unsigned long resource_mask; - unsigned long flags, i; + unsigned long i; u32 num_cores; resource_mask = adev->logged_resources; num_cores = adev->hw_cfg.dsp_cores; - spin_lock_irqsave(&adev->trace_lock, flags); + scoped_guard(spinlock_irqsave, &adev->trace_lock) { + /* Gather any remaining logs. */ + for_each_set_bit(i, &resource_mask, num_cores) { + msg.log.core = i; + avs_dsp_op(adev, log_buffer_status, &msg); + } - /* Gather any remaining logs. */ - for_each_set_bit(i, &resource_mask, num_cores) { - msg.log.core = i; - avs_dsp_op(adev, log_buffer_status, &msg); + kfifo_free(&adev->trace_fifo); } - kfifo_free(&adev->trace_fifo); - - spin_unlock_irqrestore(&adev->trace_lock, flags); - module_put(adev->dev->driver->owner); return 0; } diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index c0feb9edd7f6..39b0de9831da 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/cleanup.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/slab.h> #include <sound/hdaudio_ext.h> @@ -99,39 +100,39 @@ static void avs_dsp_recovery(struct avs_dev *adev) 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; + scoped_guard(mutex, &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) + card = acomp->base->card; + if (!card) continue; - for_each_pcm_streams(dir) { - struct snd_pcm_substream *substream; + for_each_card_rtds(card, rtd) { + struct snd_pcm *pcm; + int dir; - substream = pcm->streams[dir].substream; - if (!substream || !substream->runtime) + pcm = rtd->pcm; + if (!pcm || rtd->dai_link->no_pcm) continue; - /* No need for _irq() as we are in nonatomic context. */ - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); - snd_pcm_stream_unlock(substream); + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream; + + substream = pcm->streams[dir].substream; + if (!substream || !substream->runtime) + continue; + + /* No need for _irq() as we are in nonatomic context. */ + snd_pcm_stream_lock(substream); + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + snd_pcm_stream_unlock(substream); + } } } } - mutex_unlock(&adev->comp_list_mutex); /* forcibly shutdown all cores */ core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0); @@ -397,7 +398,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request if (!ipc->ready) return -EPERM; - mutex_lock(&ipc->msg_mutex); + guard(mutex)(&ipc->msg_mutex); spin_lock(&ipc->rx_lock); avs_ipc_msg_init(ipc, reply); @@ -412,7 +413,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request /* Same treatment as on exception, just stack_dump=0. */ avs_dsp_exception_caught(adev, &msg); } - goto exit; + return ret; } ret = ipc->rx.rsp.status; @@ -436,8 +437,6 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request memcpy(reply->data, ipc->rx.data, reply->size); } -exit: - mutex_unlock(&ipc->msg_mutex); return ret; } @@ -501,7 +500,7 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req struct avs_ipc *ipc = adev->ipc; int ret; - mutex_lock(&ipc->msg_mutex); + guard(mutex)(&ipc->msg_mutex); spin_lock(&ipc->rx_lock); avs_ipc_msg_init(ipc, NULL); @@ -522,8 +521,6 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n", name, request->glb.primary, request->glb.ext.val, ret); - mutex_unlock(&ipc->msg_mutex); - return ret; } diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 353e343b1d28..bebdc79ec88e 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/cleanup.h> #include <linux/firmware.h> #include <linux/module.h> #include <linux/slab.h> @@ -630,15 +631,15 @@ static int avs_load_firmware(struct avs_dev *adev, bool purge) 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; + scoped_guard(mutex, &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; + 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); diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index 2291f9728a54..213d6ecdd7cc 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/cleanup.h> #include <linux/acpi.h> #include <acpi/nhlt.h> #include <sound/pcm_params.h> @@ -69,16 +70,13 @@ avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id) if (!template) return NULL; - spin_lock(&adev->path_list_lock); + guard(spinlock)(&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); + if (path->template->owner == template) return path; - } } - spin_unlock(&adev->path_list_lock); return NULL; } @@ -1305,7 +1303,7 @@ void avs_path_free(struct avs_path *path) struct avs_path *cpath, *csave; struct avs_dev *adev = path->owner; - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* Free all condpaths this path spawned. */ list_for_each_entry_safe(cpath, csave, &path->source_list, source_node) @@ -1314,8 +1312,6 @@ void avs_path_free(struct avs_path *path) avs_condpath_free(path->owner, cpath); avs_path_free_unlocked(path); - - mutex_unlock(&adev->path_mutex); } struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, @@ -1334,13 +1330,13 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, } /* Serialize path and its components creation. */ - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* Satisfy needs of avs_path_find_tplg(). */ - mutex_lock(&adev->comp_list_mutex); + guard(mutex)(&adev->comp_list_mutex); path = avs_path_create_unlocked(adev, dma_id, variant); if (IS_ERR(path)) - goto exit; + return path; ret = avs_condpaths_walk_all(adev, path); if (ret) { @@ -1348,10 +1344,6 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, path = ERR_PTR(ret); } -exit: - mutex_unlock(&adev->comp_list_mutex); - mutex_unlock(&adev->path_mutex); - return path; } @@ -1496,15 +1488,13 @@ static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path) { struct avs_path *cpath; - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* If either source or sink stops, so do the attached conditional paths. */ list_for_each_entry(cpath, &path->source_list, source_node) avs_condpath_pause(adev, cpath); list_for_each_entry(cpath, &path->sink_list, sink_node) avs_condpath_pause(adev, cpath); - - mutex_unlock(&adev->path_mutex); } int avs_path_pause(struct avs_path *path) @@ -1560,7 +1550,7 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t { struct avs_path *cpath; - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* Run conditional paths only if source and sink are both running. */ list_for_each_entry(cpath, &path->source_list, source_node) @@ -1572,8 +1562,6 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t if (cpath->source->state == AVS_PPL_STATE_RUNNING && cpath->sink->state == AVS_PPL_STATE_RUNNING) avs_condpath_run(adev, cpath, trigger); - - mutex_unlock(&adev->path_mutex); } int avs_path_run(struct avs_path *path, int trigger) diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 797b9c9163b4..2b886fae8209 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -958,7 +958,7 @@ static const struct file_operations topology_name_fops = { 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); + struct avs_dev *adev = to_avs_dev(acomp->base->dev); int ret; if (!tplg->num_libs) @@ -1387,25 +1387,28 @@ int avs_register_component(struct device *dev, const char *name, struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) { struct avs_soc_component *acomp; - int ret; + const char *comp_name; acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); if (!acomp) return -ENOMEM; - acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL); - if (!acomp->base.name) + acomp->base = snd_soc_component_alloc(dev); + if (!acomp->base) + return -ENOMEM; + + comp_name = devm_kstrdup(dev, name, GFP_KERNEL); + if (!comp_name) return -ENOMEM; INIT_LIST_HEAD(&acomp->node); drv->use_dai_pcm_id = !obsolete_card_names; - ret = snd_soc_component_initialize(&acomp->base, drv, dev); - if (ret < 0) - return ret; + snd_soc_component_set_name(acomp->base, comp_name); + snd_soc_component_set_priv(acomp->base, acomp); - return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); + return snd_soc_register_component(acomp->base, drv, cpu_dais, num_cpu_dais); } static struct snd_soc_dai_driver dmic_cpu_dais[] = { diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 74096236984a..2e4063b743ba 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -296,19 +296,19 @@ static const struct snd_soc_component_driver avs_probe_component_driver = { int avs_register_probe_component(struct avs_dev *adev, const char *name) { struct snd_soc_component *component; - int ret; + const char *comp_name; - component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL); + component = snd_soc_component_alloc(adev->dev); if (!component) return -ENOMEM; - component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL); - if (!component->name) + comp_name = devm_kstrdup(adev->dev, name, GFP_KERNEL); + if (!comp_name) return -ENOMEM; - ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev); - if (ret) - return ret; + snd_soc_component_set_name(component, comp_name); - return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); + return snd_soc_register_component(component, + &avs_probe_component_driver, + probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); } diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 9033f683393c..673ac31f2fea 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -2222,7 +2222,7 @@ struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp) int avs_load_topology(struct snd_soc_component *comp, const char *filename) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int ret; ret = request_firmware(&fw, filename, comp->dev); @@ -2235,7 +2235,6 @@ int avs_load_topology(struct snd_soc_component *comp, const char *filename) if (ret < 0) dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret); - release_firmware(fw); return ret; } diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c index ee36725ac731..ea14ec173855 100644 --- a/sound/soc/intel/avs/utils.c +++ b/sound/soc/intel/avs/utils.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/cleanup.h> #include <linux/firmware.h> #include <linux/kfifo.h> #include <linux/slab.h> @@ -48,13 +49,12 @@ int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_mo { int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_entry_index(adev, uuid); if (idx >= 0) memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - mutex_unlock(&adev->modres_mutex); return (idx < 0) ? idx : 0; } @@ -62,13 +62,12 @@ int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_modu { int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); if (idx >= 0) memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - mutex_unlock(&adev->modres_mutex); return (idx < 0) ? idx : 0; } @@ -86,13 +85,12 @@ bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id) bool ret = false; int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); if (idx >= 0) ret = ida_is_empty(adev->mod_idas[idx]); - mutex_unlock(&adev->modres_mutex); return ret; } @@ -163,68 +161,57 @@ int avs_module_info_init(struct avs_dev *adev, bool purge) if (ret) return AVS_IPC_RET(ret); - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); ret = avs_module_ida_alloc(adev, info, purge); if (ret < 0) { dev_err(adev->dev, "initialize module idas failed: %d\n", ret); - goto exit; + return ret; } /* Refresh current information with newly received table. */ kfree(adev->mods_info); adev->mods_info = info; -exit: - mutex_unlock(&adev->modres_mutex); return ret; } void avs_module_info_free(struct avs_dev *adev) { - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); avs_module_ida_destroy(adev); kfree(adev->mods_info); adev->mods_info = NULL; - - mutex_unlock(&adev->modres_mutex); } int avs_module_id_alloc(struct avs_dev *adev, u16 module_id) { - int ret, idx, max_id; + int idx, max_id; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); if (idx == -ENOENT) { dev_err(adev->dev, "invalid module id: %d", module_id); - ret = -EINVAL; - goto exit; + return -EINVAL; } max_id = adev->mods_info->entries[idx].instance_max_count - 1; - ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); -exit: - mutex_unlock(&adev->modres_mutex); - return ret; + + return ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); } void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id) { int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); - if (idx == -ENOENT) { + if (idx == -ENOENT) dev_err(adev->dev, "invalid module id: %d", module_id); - goto exit; - } - - ida_free(adev->mod_idas[idx], instance_id); -exit: - mutex_unlock(&adev->modres_mutex); + else + ida_free(adev->mod_idas[idx], instance_id); } /* diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index c527d575d1ed..72fe013fffe8 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1288,7 +1288,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_dai; } - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); if (ret < 0) goto err_end; @@ -1483,12 +1483,12 @@ static int mc_probe(struct platform_device *pdev) dmi_check_system(sof_sdw_quirk_table); if (quirk_override != -1) { - dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", sof_sdw_quirk, quirk_override); sof_sdw_quirk = quirk_override; } - log_quirks(card->dev); + log_quirks(&pdev->dev); ctx->mc_quirk = sof_sdw_quirk; /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ @@ -1507,13 +1507,13 @@ static int mc_probe(struct platform_device *pdev) for (i = 0; i < ctx->codec_info_list_count; i++) amp_num += codec_info_list[i].amp_num; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, " cfg-amp:%d", amp_num); if (!card->components) return -ENOMEM; if (mach->mach_params.dmic_num) { - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s mic:dmic cfg-mics:%d", card->components, mach->mach_params.dmic_num); @@ -1524,7 +1524,7 @@ static int mc_probe(struct platform_device *pdev) /* Register the card */ ret = devm_snd_soc_register_card(card->dev, card); if (ret) { - dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); asoc_sdw_mc_dailink_exit_loop(card); return ret; } @@ -1542,8 +1542,8 @@ static void mc_remove(struct platform_device *pdev) } static const struct platform_device_id mc_id_table[] = { - { "sof_sdw", }, - {} + { .name = "sof_sdw" }, + { } }; MODULE_DEVICE_TABLE(platform, mc_id_table); diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 3881164422b8..f68807c454c9 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -139,9 +139,7 @@ int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, int catpt_first_boot_firmware(struct catpt_dev *cdev); int catpt_boot_firmware(struct catpt_dev *cdev, bool restore); -int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan); -int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan); -int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan); +int catpt_store_firmware_context(struct catpt_dev *cdev); int catpt_coredump(struct catpt_dev *cdev); #include <sound/memalloc.h> diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index b176aebea9d5..e36eea8b5408 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -28,44 +28,17 @@ static int catpt_do_suspend(struct device *dev) { struct catpt_dev *cdev = dev_get_drvdata(dev); - struct dma_chan *chan; int ret; - chan = catpt_dma_request_config_chan(cdev); - if (IS_ERR(chan)) - return PTR_ERR(chan); - memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx)); ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx); - if (ret) { - ret = CATPT_IPC_RET(ret); - goto release_dma_chan; - } - - ret = catpt_dsp_stall(cdev, true); if (ret) - goto release_dma_chan; - - ret = catpt_store_memdumps(cdev, chan); - if (ret) { - dev_err(cdev->dev, "store memdumps failed: %d\n", ret); - goto release_dma_chan; - } + return CATPT_IPC_RET(ret); - ret = catpt_store_module_states(cdev, chan); - if (ret) { - dev_err(cdev->dev, "store module states failed: %d\n", ret); - goto release_dma_chan; - } - - ret = catpt_store_streams_context(cdev, chan); - if (ret) - dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); - -release_dma_chan: - dma_release_channel(chan); + ret = catpt_store_firmware_context(cdev); if (ret) return ret; + return catpt_dsp_power_down(cdev); } diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index c577f2e17ddf..724cbe337db4 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -26,7 +26,7 @@ struct catpt_fw_hdr { u32 reserved[4]; } __packed; -struct catpt_fw_mod_hdr { +struct catpt_fw_module_hdr { char signature[FW_SIGNATURE_SIZE]; u32 mod_size; u32 blocks; @@ -81,7 +81,7 @@ catpt_request_region(struct resource *root, resource_size_t size) return __request_region(root, addr, size, NULL, 0); } -int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) +static int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) { struct catpt_stream_runtime *stream; @@ -108,7 +108,7 @@ int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } -int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) +static int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) { int i; @@ -138,7 +138,7 @@ int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } -int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) +static int catpt_store_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) { int i; @@ -171,6 +171,39 @@ int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } +int catpt_store_firmware_context(struct catpt_dev *cdev) +{ + struct dma_chan *chan; + int ret; + + chan = catpt_dma_request_config_chan(cdev); + if (IS_ERR(chan)) + return PTR_ERR(chan); + + ret = catpt_dsp_stall(cdev, true); + if (ret) + goto exit; + + ret = catpt_store_dram_data(cdev, chan); + if (ret) { + dev_err(cdev->dev, "store memdumps failed: %d\n", ret); + goto exit; + } + + ret = catpt_store_module_states(cdev, chan); + if (ret) { + dev_err(cdev->dev, "store module states failed: %d\n", ret); + goto exit; + } + + ret = catpt_store_streams_context(cdev, chan); + if (ret) + dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); +exit: + dma_release_channel(chan); + return ret; +} + static int catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) { @@ -199,7 +232,7 @@ catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } -static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) +static int catpt_restore_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) { int i; @@ -234,9 +267,9 @@ static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } -static int catpt_restore_fwimage(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_block_hdr *blk) +static int catpt_restore_dram_rodata(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_block_hdr *blk) { struct resource r1 = {}; int i; @@ -324,28 +357,26 @@ static int catpt_load_block(struct catpt_dev *cdev, static int catpt_restore_basefw(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_mod_hdr *basefw) + struct catpt_fw_module_hdr *basefw) { - u32 offset = sizeof(*basefw); + u32 off = sizeof(*basefw); int ret, i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, basefw, sizeof(*basefw), false); - /* restore basefw image */ + /* Restore IRAM and .rodata for DRAM based on the firmware image. */ for (i = 0; i < basefw->blocks; i++) { struct catpt_fw_block_hdr *blk; - blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset); + blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + off); switch (blk->ram_type) { case CATPT_RAM_TYPE_IRAM: - ret = catpt_load_block(cdev, chan, paddr + offset, - blk, false); + ret = catpt_load_block(cdev, chan, paddr + off, blk, false); break; default: - ret = catpt_restore_fwimage(cdev, chan, paddr + offset, - blk); + ret = catpt_restore_dram_rodata(cdev, chan, paddr + off, blk); break; } @@ -354,11 +385,11 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, return ret; } - offset += sizeof(*blk) + blk->size; + off += sizeof(*blk) + blk->size; } - /* then proceed with memory dumps */ - ret = catpt_restore_memdumps(cdev, chan); + /* Then proceed with DRAM .data saved before D3. */ + ret = catpt_restore_dram_data(cdev, chan); if (ret) dev_err(cdev->dev, "restore memdumps failed: %d\n", ret); @@ -367,9 +398,9 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, static int catpt_restore_module(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_mod_hdr *mod) + struct catpt_fw_module_hdr *mod) { - u32 offset = sizeof(*mod); + u32 off = sizeof(*mod); int i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, @@ -379,7 +410,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, struct catpt_fw_block_hdr *blk; int ret; - blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); + blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); switch (blk->ram_type) { case CATPT_RAM_TYPE_INSTANCE: @@ -390,7 +421,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, ALIGN(blk->size, 4)); break; default: - ret = catpt_load_block(cdev, chan, paddr + offset, + ret = catpt_load_block(cdev, chan, paddr + off, blk, false); break; } @@ -400,7 +431,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, return ret; } - offset += sizeof(*blk) + blk->size; + off += sizeof(*blk) + blk->size; } return 0; @@ -408,10 +439,10 @@ static int catpt_restore_module(struct catpt_dev *cdev, static int catpt_load_module(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_mod_hdr *mod) + struct catpt_fw_module_hdr *mod) { struct catpt_module_type *type; - u32 offset = sizeof(*mod); + u32 off = sizeof(*mod); int i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, @@ -423,9 +454,9 @@ static int catpt_load_module(struct catpt_dev *cdev, struct catpt_fw_block_hdr *blk; int ret; - blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); + blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); - ret = catpt_load_block(cdev, chan, paddr + offset, blk, true); + ret = catpt_load_block(cdev, chan, paddr + off, blk, true); if (ret) { dev_err(cdev->dev, "load block failed: %d\n", ret); return ret; @@ -440,7 +471,7 @@ static int catpt_load_module(struct catpt_dev *cdev, type->state_size = blk->size; } - offset += sizeof(*blk) + blk->size; + off += sizeof(*blk) + blk->size; } /* init module type static info */ @@ -457,17 +488,17 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, struct catpt_fw_hdr *fw) { - u32 offset = sizeof(*fw); + u32 off = sizeof(*fw); int i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, fw, sizeof(*fw), false); for (i = 0; i < fw->modules; i++) { - struct catpt_fw_mod_hdr *mod; + struct catpt_fw_module_hdr *mod; int ret; - mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); + mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); if (strncmp(fw->signature, mod->signature, FW_SIGNATURE_SIZE)) { dev_err(cdev->dev, "module signature mismatch\n"); @@ -479,12 +510,10 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, switch (mod->module_id) { case CATPT_MODID_BASE_FW: - ret = catpt_restore_basefw(cdev, chan, paddr + offset, - mod); + ret = catpt_restore_basefw(cdev, chan, paddr + off, mod); break; default: - ret = catpt_restore_module(cdev, chan, paddr + offset, - mod); + ret = catpt_restore_module(cdev, chan, paddr + off, mod); break; } @@ -493,7 +522,7 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, return ret; } - offset += sizeof(*mod) + mod->mod_size; + off += sizeof(*mod) + mod->mod_size; } return 0; @@ -503,17 +532,17 @@ static int catpt_load_firmware(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, struct catpt_fw_hdr *fw) { - u32 offset = sizeof(*fw); + u32 off = sizeof(*fw); int i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, fw, sizeof(*fw), false); for (i = 0; i < fw->modules; i++) { - struct catpt_fw_mod_hdr *mod; + struct catpt_fw_module_hdr *mod; int ret; - mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); + mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); if (strncmp(fw->signature, mod->signature, FW_SIGNATURE_SIZE)) { dev_err(cdev->dev, "module signature mismatch\n"); @@ -523,44 +552,40 @@ static int catpt_load_firmware(struct catpt_dev *cdev, if (mod->module_id > CATPT_MODID_LAST) return -EINVAL; - ret = catpt_load_module(cdev, chan, paddr + offset, mod); + ret = catpt_load_module(cdev, chan, paddr + off, mod); if (ret) { dev_err(cdev->dev, "load module failed: %d\n", ret); return ret; } - offset += sizeof(*mod) + mod->mod_size; + off += sizeof(*mod) + mod->mod_size; } return 0; } -static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, - const char *name, const char *signature, - bool restore) +static int catpt_request_load_firmware(struct catpt_dev *cdev, struct dma_chan *chan, + const char *name, bool restore) { struct catpt_fw_hdr *fw; - struct firmware *img; dma_addr_t paddr; void *vaddr; int ret; - ret = request_firmware((const struct firmware **)&img, name, cdev->dev); + const struct firmware *img __free(firmware) = NULL; + ret = request_firmware(&img, name, cdev->dev); if (ret) return ret; fw = (struct catpt_fw_hdr *)img->data; - if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) { + if (strncmp(fw->signature, FW_SIGNATURE, FW_SIGNATURE_SIZE)) { dev_err(cdev->dev, "firmware signature mismatch\n"); - ret = -EINVAL; - goto release_fw; + return -EINVAL; } vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL); - if (!vaddr) { - ret = -ENOMEM; - goto release_fw; - } + if (!vaddr) + return -ENOMEM; memcpy(vaddr, img->data, img->size); fw = (struct catpt_fw_hdr *)vaddr; @@ -570,12 +595,10 @@ static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, ret = catpt_load_firmware(cdev, chan, paddr, fw); dma_free_coherent(cdev->dev, img->size, vaddr, paddr); -release_fw: - release_firmware(img); return ret; } -static int catpt_load_images(struct catpt_dev *cdev, bool restore) +static int catpt_request_dma_load_firmware(struct catpt_dev *cdev, bool restore) { struct dma_chan *chan; int ret; @@ -584,8 +607,7 @@ static int catpt_load_images(struct catpt_dev *cdev, bool restore) if (IS_ERR(chan)) return PTR_ERR(chan); - ret = catpt_load_image(cdev, chan, cdev->spec->fw_name, - FW_SIGNATURE, restore); + ret = catpt_request_load_firmware(cdev, chan, cdev->spec->fw_name, restore); if (ret) goto release_dma_chan; @@ -605,7 +627,7 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) catpt_dsp_stall(cdev, true); - ret = catpt_load_images(cdev, restore); + ret = catpt_request_dma_load_firmware(cdev, restore); if (ret) { dev_err(cdev->dev, "load binaries failed: %d\n", ret); return ret; @@ -619,10 +641,10 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) if (!ret) { dev_err(cdev->dev, "firmware ready timeout\n"); return -ETIMEDOUT; + } /* Wake up does not mean FW is ready, an exception could occur. */ - } else if (!cdev->ipc.ready) { + if (!cdev->ipc.ready) return -EREMOTEIO; - } /* update sram pg & clock once done booting */ catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 8fb0efb67eb1..bbba0d959ea2 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -75,8 +75,8 @@ static struct catpt_stream_template *catpt_topology[] = { static struct catpt_stream_template * catpt_get_stream_template(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); enum catpt_stream_type type; type = cpu_dai->driver->id; @@ -159,11 +159,11 @@ static void catpt_stream_read_position(struct catpt_dev *cdev, static void catpt_arrange_page_table(struct snd_pcm_substream *substream, struct snd_dma_buffer *pgtbl) { - struct snd_pcm_runtime *rtm = substream->runtime; + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream); int i, pages; - pages = snd_sgbuf_aligned_pages(rtm->dma_bytes); + pages = snd_sgbuf_aligned_pages(runtime->dma_bytes); for (i = 0; i < pages; i++) { u32 pfn, offset; @@ -386,7 +386,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_pcm_runtime *rtm = substream->runtime; + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_dma_buffer *dmab; struct catpt_stream_runtime *stream; struct catpt_audio_format afmt; @@ -412,8 +412,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, memset(&rinfo, 0, sizeof(rinfo)); rinfo.page_table_addr = stream->pgtbl.addr; - rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE); - rinfo.size = rtm->dma_bytes; + rinfo.num_pages = DIV_ROUND_UP(runtime->dma_bytes, PAGE_SIZE); + rinfo.size = runtime->dma_bytes; rinfo.offset = 0; rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0)); @@ -544,11 +544,11 @@ void catpt_stream_update_position(struct catpt_dev *cdev, struct catpt_notify_position *pos) { struct snd_pcm_substream *substream = stream->substream; - struct snd_pcm_runtime *r = substream->runtime; + struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t dsppos, newpos; int ret; - dsppos = bytes_to_frames(r, pos->stream_position); + dsppos = bytes_to_frames(runtime, pos->stream_position); if (!stream->prepared) goto exit; @@ -556,8 +556,8 @@ void catpt_stream_update_position(struct catpt_dev *cdev, if (stream->template->type != CATPT_STRM_TYPE_RENDER) goto exit; - if (dsppos >= r->buffer_size / 2) - newpos = r->buffer_size / 2; + if (dsppos >= runtime->buffer_size / 2) + newpos = runtime->buffer_size / 2; else newpos = 0; /* @@ -565,7 +565,7 @@ void catpt_stream_update_position(struct catpt_dev *cdev, * (buffer half consumed) update wp to allow stream progression. */ ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id, - frames_to_bytes(r, newpos), + frames_to_bytes(runtime, newpos), false, false); if (ret) { dev_err(cdev->dev, "update position for stream %d failed: %d\n", @@ -600,11 +600,11 @@ static const struct snd_pcm_hardware catpt_pcm_hardware = { }; static int catpt_component_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtm) + struct snd_soc_pcm_runtime *rtd) { struct catpt_dev *cdev = dev_get_drvdata(component->dev); - snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV_SG, cdev->dev, catpt_pcm_hardware.buffer_bytes_max, catpt_pcm_hardware.buffer_bytes_max); @@ -615,9 +615,9 @@ static int catpt_component_pcm_new(struct snd_soc_component *component, static int catpt_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - if (!rtm->dai_link->no_pcm) + if (!rtd->dai_link->no_pcm) snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); return 0; } @@ -626,13 +626,13 @@ static snd_pcm_uframes_t catpt_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct catpt_stream_runtime *stream; struct catpt_dev *cdev = dev_get_drvdata(component->dev); u32 pos; - if (rtm->dai_link->no_pcm) + if (rtd->dai_link->no_pcm) return 0; stream = snd_soc_dai_get_dma_data(cpu_dai, substream); @@ -650,10 +650,10 @@ static const struct snd_soc_dai_ops catpt_fe_dai_ops = { .trigger = catpt_dai_trigger, }; -static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, +static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); struct catpt_ssp_device_format devfmt; struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; @@ -871,8 +871,7 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol) return CATPT_IPC_RET(ret); } -static int catpt_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int catpt_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = CATPT_CHANNELS_MAX; @@ -1073,18 +1072,14 @@ int catpt_arm_stream_templates(struct catpt_dev *cdev) int catpt_register_plat_component(struct catpt_dev *cdev) { struct snd_soc_component *component; - int ret; - component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL); + component = snd_soc_component_alloc(cdev->dev); if (!component) return -ENOMEM; - ret = snd_soc_component_initialize(component, &catpt_comp_driver, - cdev->dev); - if (ret) - return ret; + snd_soc_component_set_name(component, catpt_comp_driver.name); - component->name = catpt_comp_driver.name; - return snd_soc_add_component(component, dai_drivers, - ARRAY_SIZE(dai_drivers)); + return snd_soc_register_component(component, + &catpt_comp_driver, + dai_drivers, ARRAY_SIZE(dai_drivers)); } diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 59bfd5248819..1033f2d45c8a 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -572,6 +572,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .links = arl_rt711_l0_rt1316_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-rt711-l0-rt1316-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(3), @@ -600,12 +601,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .links = arl_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-rt711.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = 0x1, /* link0 required */ .links = arl_sdca_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-rt711-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2), diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c index 937a74a5d523..3b9758f73238 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -717,24 +717,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_3_in_1_sdca, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt711-l0-rt1316-l23-rt714-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2) | BIT(3), .links = lnl_cs42l43_l0_cs35l56_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2) | BIT(3), .links = lnl_cs42l43_l2_cs35l56x6_l13, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-cs42l43-l2-cs35l56x6-l13.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(3), .links = lnl_cs42l43_l0_cs35l56_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -748,12 +752,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt711.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2) | BIT(3), .links = lnl_712_only, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -766,19 +772,22 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .link_mask = GENMASK(2, 0), .links = lnl_sdw_rt1318_l12_rt714_l0, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg" + .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), .links = lnl_sdw_rt1320_l12_rt714_l0, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg" + .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(1), .links = lnl_sdw_rt713_l0_rt1318_l1, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg" + .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2), diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index 2e4222456f27..596582053d93 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -1321,60 +1321,70 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = tac5572_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tac5572.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = tac5672_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tac5672.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = tac5682_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tac5682.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = tas2783_link0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tas2783.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = tas2883_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tas2883.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(3, 0), .links = mtl_rt713_l0_rt1316_l12_rt1713_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12-rt1713-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(3, 0), .links = mtl_rt713_l0_rt1318_l12_rt1713_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l12-rt1713-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(1) | BIT(3), .links = mtl_rt713_l0_rt1318_l1_rt1713_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l1-rt1713-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), .links = mtl_rt713_l0_rt1316_l12, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(3) | BIT(0), .links = mtl_712_l0_1712_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -1395,7 +1405,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .link_mask = GENMASK(2, 0), .links = mtl_sdw_rt1318_l12_rt714_l0, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" + .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2) | BIT(3), @@ -1455,12 +1466,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = mtl_3_in_1_sdca, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l23-rt714-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = 0x9, /* 2 active links required */ .links = mtl_rt711_l0_rt1316_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -1474,18 +1487,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = mtl_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt711.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2), .links = rt5682_link2_max98373_link0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-sdw-rt5682-l2-max98373-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2), .links = cs42l42_link0_max98363_link2, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index f7694b2a2b02..756bbf82a326 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -492,7 +492,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .links = ptl_rt722_l0_rt1320_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt722-l0-rt1320-l23.tplg", - .get_function_tplg_files = sof_sdw_get_tplg_files, + .machine_check = snd_soc_acpi_intel_no_function_topology, }, { .link_mask = BIT(1) | BIT(2), @@ -522,14 +522,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .links = ptl_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt711.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(3), .links = ptl_sdw_rt712_vb_l3_rt1320_l3, .drv_name = "sof_sdw", - .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .machine_check = snd_soc_acpi_intel_rt712_vb_no_function_topology, .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg", - .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c index 7caabc501b16..9fdeb6ad96ac 100644 --- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c @@ -53,6 +53,14 @@ bool snd_soc_acpi_intel_no_function_topology(void *arg) } EXPORT_SYMBOL_NS(snd_soc_acpi_intel_no_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); +bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg) +{ + return snd_soc_acpi_intel_sdca_is_device_rt712_vb(arg) && + snd_soc_acpi_intel_no_function_topology(arg); +} +EXPORT_SYMBOL_NS(snd_soc_acpi_intel_rt712_vb_no_function_topology, + "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); + MODULE_DESCRIPTION("ASoC ACPI Intel SDCA quirks"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("SND_SOC_SDCA"); diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h index 2ea0a1881c4b..60b665ab5924 100644 --- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h @@ -11,5 +11,6 @@ bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg); bool snd_soc_acpi_intel_no_function_topology(void *arg); +bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg); #endif diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index 6659e8060ef3..48f132f01878 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -873,6 +873,8 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) if (kmb_i2s->use_pio) { irq = platform_get_irq_optional(pdev, 0); + if (irq == -EPROBE_DEFER) + return irq; if (irq > 0) { ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, pdev->name, kmb_i2s); diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index 7910d5d9ac4f..25cd12eab4b1 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -2,24 +2,131 @@ // // Loongson ASoC Audio Machine driver // -// Copyright (C) 2023 Loongson Technology Corporation Limited +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited // Author: Yingkun Meng <mengyingkun@loongson.cn> +// Binbin Zhou <zhoubinbin@loongson.cn> // -#include <linux/module.h> -#include <sound/soc.h> -#include <sound/soc-acpi.h> #include <linux/acpi.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> #include <linux/pci.h> +#include <sound/jack.h> #include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> static char codec_name[SND_ACPI_I2C_ID_LEN]; struct loongson_card_data { struct snd_soc_card snd_card; unsigned int mclk_fs; + struct gpio_desc *gpiod_hp_det; + struct gpio_desc *gpiod_hp_ctl; + struct gpio_desc *gpiod_spkr_en; + const struct loongson_card_config *cfg; +}; + +struct loongson_card_config { + unsigned int fmt; + bool add_hp_jack; + bool add_dapm_widgets; + bool add_dapm_routes; +}; + +static const struct loongson_card_config ls2k1000_card_config = { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC, + .add_hp_jack = false, + .add_dapm_widgets = false, + .add_dapm_routes = false, +}; + +static const struct loongson_card_config ls2k0300_forever_pi_card_config = { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, + .add_hp_jack = false, + .add_dapm_widgets = false, + .add_dapm_routes = false, +}; + +static const struct loongson_card_config ls2k0300_dl2k0300b_card_config = { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, + .add_hp_jack = true, + .add_dapm_widgets = true, + .add_dapm_routes = true, +}; + +static int tegra_machine_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm); + struct loongson_card_data *priv = snd_soc_card_get_drvdata(card); + + if (!snd_soc_dapm_widget_name_cmp(w, "Speaker")) + gpiod_set_value_cansleep(priv->gpiod_spkr_en, + SND_SOC_DAPM_EVENT_ON(event)); + + if (!snd_soc_dapm_widget_name_cmp(w, "Headphone")) + gpiod_set_value_cansleep(priv->gpiod_hp_ctl, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget loongson_aosc_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", tegra_machine_event), + SND_SOC_DAPM_SPK("Speaker", tegra_machine_event), }; +/* Headphones Jack */ + +static struct snd_soc_jack loongson_asoc_hp_jack; + +static struct snd_soc_jack_pin loongson_asoc_hp_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE + }, + { + .pin = "Speaker", + .mask = SND_JACK_HEADPHONE, + .invert = 1 + }, +}; + +static struct snd_soc_jack_gpio loongson_asoc_hp_jack_gpio = { + .name = "Headphones detection", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, +}; + +static int loongson_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct loongson_card_data *ls_priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + if (!ls_priv->cfg->add_hp_jack || !ls_priv->gpiod_hp_det) + return 0; + + ret = snd_soc_card_jack_new_pins(card, "Headphones Jack", + SND_JACK_HEADPHONE, + &loongson_asoc_hp_jack, + loongson_asoc_hp_jack_pins, + ARRAY_SIZE(loongson_asoc_hp_jack_pins)); + if (ret) { + dev_err(rtd->dev, "Headphones Jack creation failed: %d\n", ret); + return ret; + } + + loongson_asoc_hp_jack_gpio.desc = ls_priv->gpiod_hp_det; + + ret = snd_soc_jack_add_gpios(&loongson_asoc_hp_jack, 1, &loongson_asoc_hp_jack_gpio); + if (ret) + dev_err(rtd->dev, "Headphone GPIO not added: %d\n", ret); + + return ret; +} + static int loongson_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -45,7 +152,7 @@ static int loongson_card_hw_params(struct snd_pcm_substream *substream, return ret; } - return 0; + return snd_soc_runtime_set_dai_fmt(rtd, ls_card->cfg->fmt); } static const struct snd_soc_ops loongson_ops = { @@ -61,8 +168,7 @@ static struct snd_soc_dai_link loongson_dai_links[] = { { .name = "Loongson Audio Port", .stream_name = "Loongson Audio", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBC_CFC, + .init = loongson_asoc_machine_init, SND_SOC_DAILINK_REG(analog), .ops = &loongson_ops, }, @@ -91,7 +197,7 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) const char *codec_dai_name; struct acpi_device *adev; struct device *phy_dev; - int i; + int i, ret; /* fixup platform name based on reference node */ adev = loongson_card_acpi_find_device(card, "cpu"); @@ -108,7 +214,9 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) return -ENOENT; snprintf(codec_name, sizeof(codec_name), "i2c-%s", acpi_dev_name(adev)); - device_property_read_string(card->dev, "codec-dai-name", &codec_dai_name); + ret = device_property_read_string(card->dev, "codec-dai-name", &codec_dai_name); + if (ret) + return ret; for (i = 0; i < card->num_links; i++) { loongson_dai_links[i].platforms->name = dev_name(phy_dev); @@ -121,16 +229,35 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) static int loongson_card_parse_of(struct loongson_card_data *data) { - struct device_node *cpu, *codec; struct snd_soc_card *card = &data->snd_card; + struct device_node *cpu, *codec; struct device *dev = card->dev; int ret, i; + data->gpiod_hp_det = devm_gpiod_get_optional(dev, "hp-det", GPIOD_IN); + if (IS_ERR(data->gpiod_hp_det)) + return PTR_ERR(data->gpiod_hp_det); + + data->gpiod_hp_ctl = devm_gpiod_get_optional(dev, "hp-ctl", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_hp_ctl)) + return PTR_ERR(data->gpiod_hp_ctl); + + data->gpiod_spkr_en = devm_gpiod_get_optional(dev, "spkr-en", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_spkr_en)) + return PTR_ERR(data->gpiod_spkr_en); + + if (data->cfg->add_dapm_routes) { + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) + return ret; + } + cpu = of_get_child_by_name(dev->of_node, "cpu"); if (!cpu) { dev_err(dev, "platform property missing or invalid\n"); return -EINVAL; } + codec = of_get_child_by_name(dev->of_node, "codec"); if (!codec) { dev_err(dev, "audio-codec property missing or invalid\n"); @@ -175,12 +302,22 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) if (!ls_priv) return -ENOMEM; + ls_priv->cfg = (const struct loongson_card_config *)device_get_match_data(dev); + if (!ls_priv->cfg) + return -EINVAL; + card = &ls_priv->snd_card; card->dev = dev; card->owner = THIS_MODULE; card->dai_link = loongson_dai_links; card->num_links = ARRAY_SIZE(loongson_dai_links); + + if (ls_priv->cfg->add_dapm_widgets) { + card->dapm_widgets = loongson_aosc_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(loongson_aosc_dapm_widgets); + } + snd_soc_card_set_drvdata(card, ls_priv); ret = device_property_read_string(dev, "model", &card->name); @@ -200,7 +337,19 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) } static const struct of_device_id loongson_asoc_dt_ids[] = { - { .compatible = "loongson,ls-audio-card" }, + /* Loongson-2K1000/Loongson-2K2000/LS7A */ + { + .compatible = "loongson,ls-audio-card", + .data = &ls2k1000_card_config + }, + { + .compatible = "loongson,ls2k0300-forever-pi-audio-card", + .data = &ls2k0300_forever_pi_card_config + }, + { + .compatible = "loongson,ls2k0300-dl2k0300b-audio-card", + .data = &ls2k0300_dl2k0300b_card_config + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); diff --git a/sound/soc/loongson/loongson_i2s_plat.c b/sound/soc/loongson/loongson_i2s_plat.c index ac054b6ce632..82d95c6644ef 100644 --- a/sound/soc/loongson/loongson_i2s_plat.c +++ b/sound/soc/loongson/loongson_i2s_plat.c @@ -2,7 +2,7 @@ // // Loongson I2S controller master mode dirver(platform device) // -// Copyright (C) 2023-2024 Loongson Technology Corporation Limited +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited // // Author: Yingkun Meng <mengyingkun@loongson.cn> // Binbin Zhou <zhoubinbin@loongson.cn> @@ -21,6 +21,7 @@ #include "loongson_i2s.h" #include "loongson_dma.h" +/* Loongson-2K1000 APBDMA routing */ #define LOONGSON_I2S_RX_DMA_OFFSET 21 #define LOONGSON_I2S_TX_DMA_OFFSET 18 @@ -30,6 +31,11 @@ #define LOONGSON_DMA3_CONF 0x3 #define LOONGSON_DMA4_CONF 0x4 +struct loongson_i2s_plat_config { + int rev_id; + int (*i2s_dma_config)(struct platform_device *pdev); +}; + static int loongson_i2s_apbdma_config(struct platform_device *pdev) { int val; @@ -47,8 +53,18 @@ static int loongson_i2s_apbdma_config(struct platform_device *pdev) return 0; } +static const struct loongson_i2s_plat_config ls2k0300_i2s_plat_config = { + .rev_id = 1, +}; + +static const struct loongson_i2s_plat_config ls2k1000_i2s_plat_config = { + .rev_id = 0, + .i2s_dma_config = loongson_i2s_apbdma_config, +}; + static int loongson_i2s_plat_probe(struct platform_device *pdev) { + const struct loongson_i2s_plat_config *plat_config; struct device *dev = &pdev->dev; struct loongson_i2s *i2s; struct resource *res; @@ -59,12 +75,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) if (!i2s) return -ENOMEM; - ret = loongson_i2s_apbdma_config(pdev); - if (ret) - return ret; + plat_config = device_get_match_data(dev); + if (!plat_config) + return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2s->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (plat_config->i2s_dma_config) { + ret = plat_config->i2s_dma_config(pdev); + if (ret) + return ret; + } + + i2s->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2s->reg_base)) return dev_err_probe(dev, PTR_ERR(i2s->reg_base), "devm_ioremap_resource failed\n"); @@ -87,11 +108,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) if (IS_ERR(i2s_clk)) return dev_err_probe(dev, PTR_ERR(i2s_clk), "clock property invalid\n"); i2s->clk_rate = clk_get_rate(i2s_clk); + i2s->rev_id = plat_config->rev_id; dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); dev_set_name(dev, LS_I2S_DRVNAME); dev_set_drvdata(dev, i2s); + if (i2s->rev_id == 1) { + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET, I2S_CTRL_RESET); + fsleep(200); + } + ret = devm_snd_soc_register_component(dev, &loongson_i2s_edma_component, &loongson_i2s_dai, 1); if (ret) @@ -102,7 +129,8 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) } static const struct of_device_id loongson_i2s_ids[] = { - { .compatible = "loongson,ls2k1000-i2s" }, + { .compatible = "loongson,ls2k0300-i2s", .data = &ls2k0300_i2s_plat_config }, + { .compatible = "loongson,ls2k1000-i2s", .data = &ls2k1000_i2s_plat_config }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, loongson_i2s_ids); diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 4af7bbb58010..224746e7664f 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -7,7 +7,7 @@ config SND_SOC_MEDIATEK config SND_SOC_MT2701 tristate "ASoC support for Mediatek MT2701 chip" - depends on ARCH_MEDIATEK + depends on ARCH_MEDIATEK || COMPILE_TEST select SND_SOC_MEDIATEK help This adds ASoC driver for Mediatek MT2701 boards diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c index 17b9ea6be604..adefbed5bbe7 100644 --- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c +++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c @@ -232,6 +232,7 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, const char *propname) { struct device_node *np = dev->of_node; + struct snd_soc_dai_link *dai_link; struct snd_soc_dai_link *parsed_dai_link; const char *dai_name = NULL; int i, j, ret, num_links, parsed_num_links = 0; @@ -254,9 +255,9 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, return ret; } dev_dbg(dev, "ASoC: Property get dai_name:%s\n", dai_name); - for (j = 0; j < card->num_links; j++) { - if (!strcmp(dai_name, card->dai_link[j].name)) { - memcpy(&parsed_dai_link[parsed_num_links++], &card->dai_link[j], + for_each_card_prelinks(card, j, dai_link) { + if (!strcmp(dai_name, dai_link->name)) { + memcpy(&parsed_dai_link[parsed_num_links++], dai_link, sizeof(struct snd_soc_dai_link)); break; } diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 3d6d7bc05b87..8c5096482520 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -315,7 +315,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) &mt8173_rt5650_priv.pll_from); if (ret) { dev_err(&pdev->dev, - "%s snd_soc_register_card fail %d\n", + "%s device_property_read_u32() fail %d\n", __func__, ret); } } diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c index ad5b9546ff63..5c41a6386204 100644 --- a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c +++ b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c @@ -385,8 +385,6 @@ static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w, if (event == SND_SOC_DAPM_PRE_PMU) { if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2) regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB8); - else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2) - regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); else regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); } diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index 8af8b0a366d5..c86348691641 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -1019,7 +1019,7 @@ static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = { .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes), }; -static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, +static int mt8192_mt6359_card_set_be_link(struct device *dev, struct snd_soc_dai_link *link, struct device_node *node, char *link_name) @@ -1027,9 +1027,9 @@ static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, int ret; if (node && strcmp(link->name, link_name) == 0) { - ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link); + ret = snd_soc_of_get_dai_link_codecs(dev, node, link); if (ret < 0) { - dev_err_probe(card->dev, ret, "get dai link codecs fail\n"); + dev_err_probe(dev, ret, "get dai link codecs fail\n"); return ret; } } @@ -1065,21 +1065,21 @@ static int mt8192_mt6359_legacy_probe(struct mtk_soc_card_data *soc_card_data) } for_each_card_prelinks(card, i, dai_link) { - ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3"); + ret = mt8192_mt6359_card_set_be_link(dev, dai_link, speaker_codec, "I2S3"); if (ret) { dev_err_probe(dev, ret, "%s set speaker_codec fail\n", dai_link->name); break; } - ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8"); + ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S8"); if (ret) { dev_err_probe(dev, ret, "%s set headset_codec fail\n", dai_link->name); break; } - ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9"); + ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S9"); if (ret) { dev_err_probe(dev, ret, "%s set headset_codec fail\n", dai_link->name); diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8196/mt8196-nau8825.c index 1d1dad86365b..dae62d684fea 100644 --- a/sound/soc/mediatek/mt8196/mt8196-nau8825.c +++ b/sound/soc/mediatek/mt8196/mt8196-nau8825.c @@ -104,8 +104,6 @@ static const struct snd_kcontrol_new mt8196_nau8825_controls[] = { #define EXT_SPK_AMP_W_NAME "Ext_Speaker_Amp" -static struct snd_soc_card mt8196_nau8825_soc_card; - static const struct snd_soc_dapm_widget mt8196_nau8825_card_widgets[] = { /* SOF Uplink */ SND_SOC_DAPM_MIXER("SOF_DMA_UL0", SND_SOC_NOPM, 0, 0, NULL, 0), diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c index 5966ca18c7c9..e9497246f37c 100644 --- a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c +++ b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c @@ -2120,19 +2120,15 @@ static int mt8365_afe_pcm_dev_probe(struct platform_device *pdev) spin_lock_init(&afe_priv->afe_ctrl_lock); mutex_init(&afe_priv->afe_clk_mutex); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - afe->base_addr = devm_ioremap_resource(&pdev->dev, res); + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(afe->base_addr)) return PTR_ERR(afe->base_addr); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { - afe_priv->afe_sram_vir_addr = - devm_ioremap_resource(&pdev->dev, res); - if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { - afe_priv->afe_sram_phy_addr = res->start; - afe_priv->afe_sram_size = resource_size(res); - } + afe_priv->afe_sram_vir_addr = + devm_platform_get_and_ioremap_resource(pdev, 1, &res); + if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { + afe_priv->afe_sram_phy_addr = res->start; + afe_priv->afe_sram_size = resource_size(res); } /* initial audio related clock */ diff --git a/sound/soc/mediatek/mt8365/mt8365-mt6357.c b/sound/soc/mediatek/mt8365/mt8365-mt6357.c index 90448df6c0b2..dceee837c67d 100644 --- a/sound/soc/mediatek/mt8365/mt8365-mt6357.c +++ b/sound/soc/mediatek/mt8365/mt8365-mt6357.c @@ -70,7 +70,8 @@ static const struct snd_soc_dapm_route mt8365_mt6357_routes[] = { static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); + struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; int ret = 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -101,7 +102,8 @@ static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) static void mt8365_mt6357_int_adda_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); + struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; int ret = 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -245,7 +247,8 @@ static struct snd_soc_dai_link mt8365_mt6357_dais[] = { static int mt8365_mt6357_gpio_probe(struct snd_soc_card *card) { - struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(card); + struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); + struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; struct device *dev = card->dev; int ret, i; @@ -294,7 +297,6 @@ static int mt8365_mt6357_dev_probe(struct mtk_soc_card_data *soc_card_data, bool struct mt8365_mt6357_priv *mach_priv; int ret; - card->dev = dev; ret = parse_dai_link_info(card); if (ret) goto err; diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile index 24078e4396b0..f9ec0ebb01f0 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile @@ -4,6 +4,8 @@ snd-soc-meson-aiu-y := aiu.o snd-soc-meson-aiu-y += aiu-acodec-ctrl.o snd-soc-meson-aiu-y += aiu-codec-ctrl.o snd-soc-meson-aiu-y += aiu-encoder-i2s.o +snd-soc-meson-aiu-y += gx-formatter.o +snd-soc-meson-aiu-y += aiu-formatter-i2s.o snd-soc-meson-aiu-y += aiu-encoder-spdif.o snd-soc-meson-aiu-y += aiu-fifo.o snd-soc-meson-aiu-y += aiu-fifo-i2s.o diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c index 3b4061508c18..58dce9f08c9d 100644 --- a/sound/soc/meson/aiu-encoder-i2s.c +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -10,14 +10,11 @@ #include <sound/soc-dai.h> #include "aiu.h" +#include "gx-formatter.h" +#include "gx-interface.h" -#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) -#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) -#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) #define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) -#define AIU_RST_SOFT_I2S_FAST BIT(0) -#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) #define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) #define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) #define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) @@ -35,49 +32,6 @@ static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component, enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); } -static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, - struct snd_pcm_hw_params *params) -{ - /* Always operate in split (classic interleaved) mode */ - unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; - - /* Reset required to update the pipeline */ - snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); - snd_soc_component_read(component, AIU_I2S_SYNC); - - switch (params_physical_width(params)) { - case 16: /* Nothing to do */ - break; - - case 32: - desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | - AIU_I2S_SOURCE_DESC_MODE_32BIT); - break; - - default: - return -EINVAL; - } - - switch (params_channels(params)) { - case 2: /* Nothing to do */ - break; - case 8: - desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; - break; - default: - return -EINVAL; - } - - snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, - AIU_I2S_SOURCE_DESC_MODE_8CH | - AIU_I2S_SOURCE_DESC_MODE_24BIT | - AIU_I2S_SOURCE_DESC_MODE_32BIT | - AIU_I2S_SOURCE_DESC_MODE_SPLIT, - desc); - - return 0; -} - static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, struct snd_pcm_hw_params *params, unsigned int bs) @@ -108,6 +62,32 @@ static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, return 0; } +/* + * Return true if the given combination of channels and sample width requires + * the bs quirk. Return false otherwise. + */ +static bool aiu_encoder_is_bs_quirk(unsigned int channels, int width) +{ + return (channels == 8) && (width == 16); +} + +static int aiu_encoder_check_bs_quirk(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); + + /* Nothing to do if the other stream doesn't exist or it's not configured yet. */ + if (!other_stream || !other_stream->channels) + return 0; + + if (aiu_encoder_is_bs_quirk(other_stream->channels, other_stream->width) != + aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) + return -EINVAL; + + return 0; +} + static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, struct snd_pcm_hw_params *params, unsigned int bs) @@ -119,7 +99,7 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, * increased by 50% to get the correct output rate. * No idea why ! */ - if (params_width(params) == 16 && params_channels(params) == 8) { + if (aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) { if (bs % 2) { dev_err(component->dev, "Cannot increase i2s divider by 50%%\n"); @@ -141,25 +121,23 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, return 0; } -static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, - struct snd_pcm_hw_params *params) +static int aiu_encoder_i2s_set_clocks(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { + struct snd_soc_component *component = dai->component; struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct gx_iface *iface = &aiu->i2s.iface; unsigned int srate = params_rate(params); unsigned int fs, bs; int ret; /* Get the oversampling factor */ - fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); + fs = DIV_ROUND_CLOSEST(iface->mclk_rate, srate); - if (fs % 64) + if ((fs % 64) || (fs == 0)) return -EINVAL; - /* Send data MSB first */ - snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, - AIU_I2S_DAC_CFG_MSB_FIRST, - AIU_I2S_DAC_CFG_MSB_FIRST); - /* Set bclk to lrlck ratio */ snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, AIU_CODEC_DAC_LRCLK_CTRL_DIV, @@ -168,10 +146,22 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, bs = fs / 64; - if (aiu->platform->has_clk_ctrl_more_i2s_div) + if (aiu->platform->has_clk_ctrl_more_i2s_div) { + /* + * The hw rules added in startup() make this unreachable in the + * sequential case, but both streams may be refined concurrently + * before either commits its config, since only ops->hw_params + * runs under the card's pcm_mutex. Re-check against the committed + * state of the other stream, which is stable under that mutex. + */ + if (aiu_encoder_check_bs_quirk(substream, params, dai)) { + dev_err(dai->dev, "bclk requirements incompatible with other stream\n"); + return -EINVAL; + } ret = aiu_encoder_i2s_set_more_div(component, params, bs); - else + } else { ret = aiu_encoder_i2s_set_legacy_div(component, params, bs); + } if (ret) return ret; @@ -188,23 +178,37 @@ static int aiu_encoder_i2s_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 gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); int ret; - /* Disable the clock while changing the settings */ - aiu_encoder_i2s_divider_enable(component, false); - - ret = aiu_encoder_i2s_setup_desc(component, params); + ret = aiu_encoder_i2s_set_clocks(substream, params, dai); if (ret) { - dev_err(dai->dev, "setting i2s desc failed\n"); + dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); return ret; } - ret = aiu_encoder_i2s_set_clocks(component, params); - if (ret) { - dev_err(dai->dev, "setting i2s clocks failed\n"); + ts->physical_width = params_physical_width(params); + ts->width = params_width(params); + ts->channels = params_channels(params); + + return 0; +} + +static int aiu_encoder_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct snd_soc_component *component = dai->component; + int ret; + + if (ts->clk_enabled) + return 0; + + ret = clk_prepare_enable(ts->iface->mclk); + if (ret) return ret; - } + + ts->clk_enabled = true; aiu_encoder_i2s_divider_enable(component, true); @@ -214,9 +218,24 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); struct snd_soc_component *component = dai->component; - aiu_encoder_i2s_divider_enable(component, false); + /* + * If this is the last substream being closed then disable the i2s + * clock divider. + */ + if (snd_soc_dai_active(dai) <= 1) + aiu_encoder_i2s_divider_enable(component, 0); + + if (ts->clk_enabled) { + clk_disable_unprepare(ts->iface->mclk); + ts->clk_enabled = false; + } + + ts->channels = 0; + ts->width = 0; + ts->physical_width = 0; return 0; } @@ -224,6 +243,8 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; + struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct gx_iface *iface = &aiu->i2s.iface; unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; unsigned int val = 0; unsigned int skew; @@ -255,9 +276,12 @@ static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) skew = 0; break; default: + dev_err(dai->dev, "unsupported dai format\n"); return -EINVAL; } + iface->fmt = fmt; + val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); snd_soc_component_update_bits(component, AIU_CLK_CTRL, AIU_CLK_CTRL_LRCLK_INVERT | @@ -272,6 +296,7 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + struct gx_iface *iface = &aiu->i2s.iface; int ret; if (WARN_ON(clk_id != 0)) @@ -280,11 +305,15 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, if (dir == SND_SOC_CLOCK_IN) return 0; - ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); - if (ret) - dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); + ret = clk_set_rate(iface->mclk, freq); + if (ret) { + dev_err(dai->dev, "Failed to set sysclk to %uHz: %d", freq, ret); + return ret; + } - return ret; + iface->mclk_rate = freq; + + return 0; } static const unsigned int hw_channels[] = {2, 8}; @@ -294,10 +323,45 @@ static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { .mask = 0, }; +static int aiu_encoder_i2s_pcm_hw_rule(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct gx_stream *other = rule->private; + struct snd_interval *ch = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + /* + * The quirk is technically based on the significant bits whereas here + * we're using the physical width for simplicity. This works because + * S16_LE is the only format supported by this encoder that has: + * significant bits = physical width = 16-bits + */ + struct snd_interval *phys_width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + struct snd_interval new_i; + + if (other->channels == 0) + return 0; + + snd_interval_any(&new_i); + + if (rule->var == SNDRV_PCM_HW_PARAM_CHANNELS) { + if (aiu_encoder_is_bs_quirk(other->channels, other->width)) + new_i.min = new_i.max = 8; + else if (snd_interval_single(phys_width) && phys_width->min == 16) + new_i.max = 2; /* Force 2ch */ + } else { /* SNDRV_PCM_HW_PARAM_SAMPLE_BITS */ + if (aiu_encoder_is_bs_quirk(other->channels, other->width)) + new_i.min = new_i.max = 16; + else if (snd_interval_single(ch) && ch->min == 8) + new_i.min = 17; /* Request physical width > 16 bits */ + } + + return snd_interval_refine(hw_param_interval(params, rule->var), &new_i); +} + static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); int ret; /* Make sure the encoder gets either 2 or 8 channels */ @@ -305,15 +369,79 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_channel_constraints); if (ret) { - dev_err(dai->dev, "adding channels constraints failed\n"); + dev_err(dai->dev, "adding channels constraints failed: %d\n", ret); return ret; } - ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); - if (ret) - dev_err(dai->dev, "failed to enable i2s clocks\n"); + /* + * If DAI supports both playback and capture streams ensure the bs-quirk is + * handled correctly. + * This is only valid for GX platforms (has_clk_ctrl_more_i2s_div=true). + */ + if (aiu->platform->has_clk_ctrl_more_i2s_div && other_stream) { + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + aiu_encoder_i2s_pcm_hw_rule, + other_stream, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (ret) + return ret; + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + aiu_encoder_i2s_pcm_hw_rule, + other_stream, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (ret) + return ret; + } - return ret; + /* + * Enable only clocks which are required for the interface internal + * logic. MCLK is enabled/disabled from the formatter and the I2S + * divider is enabled/disabled in "hw_params"/"hw_free", respectively. + */ + ret = clk_prepare_enable(aiu->i2s.clks[PCLK].clk); + if (ret) { + dev_err(dai->dev, "failed to enable PCLK: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(aiu->i2s.clks[MIXER].clk); + if (ret) { + dev_err(dai->dev, "failed to enable MIXER: %d\n", ret); + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); + return ret; + } + ret = clk_prepare_enable(aiu->i2s.clks[AOCLK].clk); + if (ret) { + dev_err(dai->dev, "failed to enable AOCLK: %d\n", ret); + clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); + return ret; + } + + /* + * We're always operating in split mode for the playback stream. + * + * This setting arguably belong to the 'aiu-formatter', but it's kept + * here for backward compatibility reason. At reset the I2S encoder + * operates in normal mode which would only support 8ch, but by default + * only 2ch are enabled. If a playback stream is started without + * changing to split mode, then the I2S encoder doesn't consume audio + * samples and the playback fails. + * Moving this to 'aiu-formatter' would cause the split mode to be set + * only when the formatter is enabled, which doesn't happen at boot as + * the default value for "HDMI CTRL SRC" is "DISABLED". + */ + ret = snd_soc_component_update_bits(dai->component, AIU_I2S_SOURCE_DESC, + AIU_I2S_SOURCE_DESC_MODE_SPLIT, + AIU_I2S_SOURCE_DESC_MODE_SPLIT); + if (ret < 0) + dev_err(dai->dev, "failed to update AIU_I2S_SOURCE_DESC: %d", ret); + + return 0; } static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, @@ -321,14 +449,89 @@ static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, { struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); - clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); + clk_disable_unprepare(aiu->i2s.clks[AOCLK].clk); + clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); +} + +static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, + struct snd_soc_dai *dai) +{ + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = gx_stream_start(ts); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + gx_stream_stop(ts); + ret = 0; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int aiu_encoder_i2s_remove_dai(struct snd_soc_dai *dai) +{ + int stream; + + for_each_pcm_streams(stream) { + struct gx_stream *ts; + + ts = snd_soc_dai_dma_data_get(dai, stream); + if (ts) + gx_stream_free(ts); + + snd_soc_dai_dma_data_set(dai, stream, NULL); + } + + return 0; +} + +static int aiu_encoder_i2s_probe_dai(struct snd_soc_dai *dai) +{ + struct aiu *aiu = snd_soc_dai_get_drvdata(dai); + struct gx_iface *iface = &aiu->i2s.iface; + int stream; + + for_each_pcm_streams(stream) { + struct gx_stream *ts; + + if (!snd_soc_dai_get_widget(dai, stream)) + continue; + + ts = gx_stream_alloc(iface); + if (!ts) { + aiu_encoder_i2s_remove_dai(dai); + return -ENOMEM; + } + snd_soc_dai_dma_data_set(dai, stream, ts); + } + + iface->mclk = aiu->i2s.clks[MCLK].clk; + iface->mclk_rate = clk_get_rate(iface->mclk); + + return 0; } const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { + .probe = aiu_encoder_i2s_probe_dai, + .remove = aiu_encoder_i2s_remove_dai, .hw_params = aiu_encoder_i2s_hw_params, + .prepare = aiu_encoder_i2s_prepare, .hw_free = aiu_encoder_i2s_hw_free, .set_fmt = aiu_encoder_i2s_set_fmt, .set_sysclk = aiu_encoder_i2s_set_sysclk, .startup = aiu_encoder_i2s_startup, .shutdown = aiu_encoder_i2s_shutdown, + .trigger = aiu_encoder_i2s_trigger, }; diff --git a/sound/soc/meson/aiu-formatter-i2s.c b/sound/soc/meson/aiu-formatter-i2s.c new file mode 100644 index 000000000000..cb554c2e7ce4 --- /dev/null +++ b/sound/soc/meson/aiu-formatter-i2s.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2026 BayLibre, SAS. +// Author: Valerio Setti <vsetti@baylibre.com> + +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "aiu.h" +#include "gx-formatter.h" + +#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) +#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) +#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) + +#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) + +static struct snd_soc_dai * +aiu_formatter_i2s_get_be(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + struct snd_soc_dai *be; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + if (p->sink->id == snd_soc_dapm_dai_in) + return (struct snd_soc_dai *)p->sink->priv; + + be = aiu_formatter_i2s_get_be(p->sink); + if (be) + return be; + } + + return NULL; +} + +static struct gx_stream * +aiu_formatter_i2s_get_stream(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dai *be = aiu_formatter_i2s_get_be(w); + + if (!be) + return NULL; + + return snd_soc_dai_dma_data_get_playback(be); +} + +static int aiu_formatter_i2s_prepare(struct regmap *map, + const struct gx_formatter_hw *quirks, + struct gx_stream *ts) +{ + /* Always operate in split (classic interleaved) mode */ + unsigned int desc = 0; + + /* + * Pipeline reset is already implemented in aiu_fifo_i2s_trigger() at + * trigger time. + */ + + switch (ts->physical_width) { + case 16: /* Nothing to do */ + break; + + case 32: + desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | + AIU_I2S_SOURCE_DESC_MODE_32BIT); + break; + + default: + return -EINVAL; + } + + switch (ts->channels) { + case 2: /* Nothing to do */ + break; + case 8: + desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; + break; + default: + return -EINVAL; + } + + regmap_update_bits(map, AIU_I2S_SOURCE_DESC, + AIU_I2S_SOURCE_DESC_MODE_8CH | + AIU_I2S_SOURCE_DESC_MODE_24BIT | + AIU_I2S_SOURCE_DESC_MODE_32BIT, + desc); + + /* Send data MSB first */ + regmap_update_bits(map, AIU_I2S_DAC_CFG, + AIU_I2S_DAC_CFG_MSB_FIRST, + AIU_I2S_DAC_CFG_MSB_FIRST); + + return 0; +} + +const struct gx_formatter_ops aiu_formatter_i2s_ops = { + .get_stream = aiu_formatter_i2s_get_stream, + .prepare = aiu_formatter_i2s_prepare, +}; diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index f2890111c1d2..535fbf7c1639 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -29,13 +29,22 @@ static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, static const struct snd_kcontrol_new aiu_spdif_encode_mux = SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); -static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { - SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, - &aiu_spdif_encode_mux), +#define AIU_WIDGET_SPDIF_SRC_SEL 0 +#define AIU_WIDGET_I2S_FORMATTER 1 + +static struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { + [AIU_WIDGET_SPDIF_SRC_SEL] = + SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, + &aiu_spdif_encode_mux), + [AIU_WIDGET_I2S_FORMATTER] = + SND_SOC_DAPM_PGA_E("I2S Formatter", SND_SOC_NOPM, 0, 0, NULL, 0, + gx_formatter_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), }; static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { - { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, + { "I2S Formatter", NULL, "I2S FIFO Playback" }, + { "I2S Encoder Playback", NULL, "I2S Formatter" }, { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, @@ -145,6 +154,7 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { .formats = AIU_FORMATS, }, .ops = &aiu_encoder_i2s_dai_ops, + .symmetric_rate = 1, }, [CPU_SPDIF_ENCODER] = { .name = "SPDIF Encoder", @@ -172,6 +182,11 @@ static const struct regmap_config aiu_regmap_cfg = { .max_register = 0x2ac, }; +static const struct gx_formatter_driver aiu_formatter_i2s_drv = { + .regmap_cfg = &aiu_regmap_cfg, + .ops = &aiu_formatter_i2s_ops, +}; + static int aiu_clk_bulk_get(struct device *dev, const char * const *ids, unsigned int num, @@ -282,6 +297,14 @@ static int aiu_probe(struct platform_device *pdev) if (ret) return ret; + /* Allocate the aiu-formatter into its widget */ + ret = gx_formatter_create(dev, &aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER], + &aiu_formatter_i2s_drv, map); + if (ret) { + dev_err(dev, "Failed to allocate aiu formatter\n"); + goto err; + } + /* Register the cpu component of the aiu */ ret = snd_soc_register_component(dev, &aiu_cpu_component, aiu_cpu_dai_drv, @@ -310,12 +333,14 @@ static int aiu_probe(struct platform_device *pdev) return 0; err: + gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); snd_soc_unregister_component(dev); return ret; } static void aiu_remove(struct platform_device *pdev) { + gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); snd_soc_unregister_component(&pdev->dev); } diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h index 0f94c8bf6081..7d0b98c1f351 100644 --- a/sound/soc/meson/aiu.h +++ b/sound/soc/meson/aiu.h @@ -7,6 +7,8 @@ #ifndef _MESON_AIU_H #define _MESON_AIU_H +#include "gx-formatter.h" + struct clk; struct clk_bulk_data; struct device; @@ -25,6 +27,7 @@ struct aiu_interface { struct clk_bulk_data *clks; unsigned int clk_num; int irq; + struct gx_iface iface; }; struct aiu_platform_data { @@ -58,6 +61,7 @@ extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops; extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops; extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops; extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops; +extern const struct gx_formatter_ops aiu_formatter_i2s_ops; #define AIU_IEC958_BPF 0x000 #define AIU_958_MISC 0x010 diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index b4dca80e15e4..c36f727af4ba 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -107,6 +107,7 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, struct snd_soc_dai_link *pad; struct snd_soc_dai_link *lb; struct snd_soc_dai_link_component *dlc; + struct device *dev = card->dev; int ret; /* extend links */ @@ -117,11 +118,11 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, pad = &card->dai_link[*index]; lb = &card->dai_link[*index + 1]; - lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name); + lb->name = devm_kasprintf(dev, GFP_KERNEL, "%s-lb", pad->name); if (!lb->name) return -ENOMEM; - dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL); + dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL); if (!dlc) return -ENOMEM; @@ -158,13 +159,14 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, struct device_node *node, struct axg_dai_link_tdm_data *be) { + struct device *dev = card->dev; char propname[32]; u32 tx, rx; int i; - be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, + be->tx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, sizeof(*be->tx_mask), GFP_KERNEL); - be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, + be->rx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, sizeof(*be->rx_mask), GFP_KERNEL); if (!be->tx_mask || !be->rx_mask) return -ENOMEM; @@ -191,7 +193,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, /* ... but the interface should at least have one direction */ if (!tx && !rx) { - dev_err(card->dev, "tdm link has no cpu slots\n"); + dev_err(dev, "tdm link has no cpu slots\n"); return -EINVAL; } @@ -207,7 +209,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, * Error if the slots can't accommodate the largest mask or * if it is just too big */ - dev_err(card->dev, "bad slot number\n"); + dev_err(dev, "bad slot number\n"); return -EINVAL; } @@ -222,8 +224,9 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, struct axg_dai_link_tdm_data *be) { struct axg_dai_link_tdm_mask *codec_mask; + struct device *dev = card->dev; - codec_mask = devm_kcalloc(card->dev, link->num_codecs, + codec_mask = devm_kcalloc(dev, link->num_codecs, sizeof(*codec_mask), GFP_KERNEL); if (!codec_mask) return -ENOMEM; @@ -249,10 +252,11 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, struct meson_card *priv = snd_soc_card_get_drvdata(card); struct snd_soc_dai_link *link = &card->dai_link[*index]; struct axg_dai_link_tdm_data *be; + struct device *dev = card->dev; int ret; /* Allocate tdm link parameters */ - be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); + be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); if (!be) return -ENOMEM; priv->link_data[*index] = be; @@ -266,7 +270,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, ret = axg_card_parse_cpu_tdm_slots(card, link, node, be); if (ret) { - dev_err(card->dev, "error parsing tdm link slots\n"); + dev_err(dev, "error parsing tdm link slots\n"); return ret; } @@ -310,9 +314,10 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, { struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; struct snd_soc_dai_link_component *cpu; + struct device *dev = card->dev; int ret; - cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); + cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); if (!cpu) return -ENOMEM; diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c index a95375b53f0a..21941ee552c5 100644 --- a/sound/soc/meson/g12a-toacodec.c +++ b/sound/soc/meson/g12a-toacodec.c @@ -312,7 +312,7 @@ static int g12a_toacodec_probe(struct platform_device *pdev) ret = device_reset(dev); if (ret) - return ret; + return dev_err_probe(dev, ret, "failed to reset device\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c index d541ca4acfaf..967109ca2b57 100644 --- a/sound/soc/meson/g12a-tohdmitx.c +++ b/sound/soc/meson/g12a-tohdmitx.c @@ -251,7 +251,7 @@ static int g12a_tohdmitx_probe(struct platform_device *pdev) ret = device_reset(dev); if (ret) - return ret; + return dev_err_probe(dev, ret, "failed to reset device\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c index b408cc2bbc91..932be0a09306 100644 --- a/sound/soc/meson/gx-card.c +++ b/sound/soc/meson/gx-card.c @@ -48,9 +48,10 @@ static int gx_card_parse_i2s(struct snd_soc_card *card, struct meson_card *priv = snd_soc_card_get_drvdata(card); struct snd_soc_dai_link *link = &card->dai_link[*index]; struct gx_dai_link_i2s_data *be; + struct device *dev = card->dev; /* Allocate i2s link parameters */ - be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); + be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); if (!be) return -ENOMEM; priv->link_data[*index] = be; @@ -81,9 +82,10 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, { struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; struct snd_soc_dai_link_component *cpu; + struct device *dev = card->dev; int ret; - cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); + cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); if (!cpu) return -ENOMEM; diff --git a/sound/soc/meson/gx-formatter.c b/sound/soc/meson/gx-formatter.c new file mode 100644 index 000000000000..311e63affb23 --- /dev/null +++ b/sound/soc/meson/gx-formatter.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2026 BayLibre, SAS. +// Author: Valerio Setti <vsetti@baylibre.com> + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> + +#include "gx-formatter.h" + +struct gx_formatter { + struct list_head list; + struct gx_stream *stream; + const struct gx_formatter_driver *drv; + bool enabled; + struct regmap *map; +}; + +static int gx_formatter_enable(struct gx_formatter *formatter) +{ + int ret; + + /* Do nothing if the formatter is already enabled */ + if (formatter->enabled) + return 0; + + /* Setup the stream parameter in the formatter */ + if (formatter->drv->ops->prepare) { + ret = formatter->drv->ops->prepare(formatter->map, + formatter->drv->quirks, + formatter->stream); + if (ret) + return ret; + } + + /* Finally, actually enable the formatter */ + if (formatter->drv->ops->enable) + formatter->drv->ops->enable(formatter->map); + + formatter->enabled = true; + + return 0; +} + +static void gx_formatter_disable(struct gx_formatter *formatter) +{ + /* Do nothing if the formatter is already disabled */ + if (!formatter->enabled) + return; + + if (formatter->drv->ops->disable) + formatter->drv->ops->disable(formatter->map); + + formatter->enabled = false; +} + +static int gx_formatter_attach(struct gx_formatter *formatter) +{ + struct gx_stream *ts = formatter->stream; + int ret = 0; + + mutex_lock(&ts->lock); + + /* Catch up if the stream is already running when we attach */ + if (ts->ready) { + ret = gx_formatter_enable(formatter); + if (ret) { + pr_err("failed to enable formatter\n"); + goto out; + } + } + + list_add_tail(&formatter->list, &ts->formatter_list); +out: + mutex_unlock(&ts->lock); + return ret; +} + +static void gx_formatter_detach(struct gx_formatter *formatter) +{ + struct gx_stream *ts = formatter->stream; + + if (!ts) + return; + + mutex_lock(&ts->lock); + list_del(&formatter->list); + mutex_unlock(&ts->lock); + + gx_formatter_disable(formatter); +} + +static int gx_formatter_power_up(struct gx_formatter *formatter, + struct snd_soc_dapm_widget *w) +{ + struct gx_stream *ts = formatter->drv->ops->get_stream(w); + int ret; + + /* + * If we don't get a stream at this stage, it would mean that the + * widget is powering up but is not attached to any backend DAI. + * It should not happen, ever ! + */ + if (WARN_ON(!ts)) + return -ENODEV; + + formatter->stream = ts; + INIT_LIST_HEAD(&formatter->list); + ret = gx_formatter_attach(formatter); + if (ret) + return ret; + + return 0; +} + +static void gx_formatter_power_down(struct gx_formatter *formatter) +{ + gx_formatter_detach(formatter); + formatter->stream = NULL; +} + +int gx_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event) +{ + struct snd_soc_component *c; + struct gx_formatter *formatter; + int ret = 0; + + c = snd_soc_dapm_to_component(w->dapm); + + if (w->priv) + formatter = w->priv; + else + formatter = snd_soc_component_get_drvdata(c); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = gx_formatter_power_up(formatter, w); + break; + + case SND_SOC_DAPM_PRE_PMD: + gx_formatter_power_down(formatter); + break; + + default: + dev_err(c->dev, "Unexpected event %d\n", event); + return -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(gx_formatter_event); + +int gx_formatter_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct gx_formatter_driver *drv; + struct gx_formatter *formatter; + void __iomem *regs; + + drv = of_device_get_match_data(dev); + if (!drv) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); + if (!formatter) + return -ENOMEM; + platform_set_drvdata(pdev, formatter); + formatter->drv = drv; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); + if (IS_ERR(formatter->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(formatter->map)); + return PTR_ERR(formatter->map); + } + + return devm_snd_soc_register_component(dev, drv->component_drv, + NULL, 0); +} +EXPORT_SYMBOL_GPL(gx_formatter_probe); + +int gx_formatter_create(struct device *dev, + struct snd_soc_dapm_widget *w, + const struct gx_formatter_driver *drv, + struct regmap *regmap) +{ + struct gx_formatter *formatter; + + formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); + if (!formatter) + return -ENOMEM; + + formatter->drv = drv; + formatter->map = regmap; + + w->priv = formatter; + + return 0; +} +EXPORT_SYMBOL_GPL(gx_formatter_create); + +int gx_stream_start(struct gx_stream *ts) +{ + struct gx_formatter *formatter; + int ret = 0; + + mutex_lock(&ts->lock); + + /* Start all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + ret = gx_formatter_enable(formatter); + if (ret) { + pr_err("failed to enable formatter\n"); + goto out; + } + } + + ts->ready = true; + +out: + mutex_unlock(&ts->lock); + return ret; +} +EXPORT_SYMBOL_GPL(gx_stream_start); + +void gx_stream_stop(struct gx_stream *ts) +{ + struct gx_formatter *formatter; + + mutex_lock(&ts->lock); + ts->ready = false; + + /* Stop all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + gx_formatter_disable(formatter); + } + + mutex_unlock(&ts->lock); +} +EXPORT_SYMBOL_GPL(gx_stream_stop); + +struct gx_stream *gx_stream_alloc(struct gx_iface *iface) +{ + struct gx_stream *ts; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts) { + INIT_LIST_HEAD(&ts->formatter_list); + mutex_init(&ts->lock); + ts->iface = iface; + } + + return ts; +} +EXPORT_SYMBOL_GPL(gx_stream_alloc); + +void gx_stream_free(struct gx_stream *ts) +{ + /* + * If the list is not empty, it would mean that one of the formatter + * widget is still powered and attached to the interface while we + * are removing the TDM DAI. It should not be possible + */ + WARN_ON(!list_empty(&ts->formatter_list)); + mutex_destroy(&ts->lock); + kfree(ts); +} +EXPORT_SYMBOL_GPL(gx_stream_free); + +MODULE_DESCRIPTION("Amlogic GX formatter driver"); +MODULE_AUTHOR("Valerio Setti <vsetti@baylibre.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/meson/gx-formatter.h b/sound/soc/meson/gx-formatter.h new file mode 100644 index 000000000000..b90b1814d79b --- /dev/null +++ b/sound/soc/meson/gx-formatter.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2026 Baylibre SAS. + * Author: Valerio Setti <vsetti@baylibre.com> + */ + +#ifndef _MESON_GX_FORMATTER_H +#define _MESON_GX_FORMATTER_H + +#include "gx-interface.h" + +struct platform_device; +struct regmap; +struct snd_soc_dapm_widget; +struct snd_kcontrol; + +struct gx_formatter_hw { + unsigned int skew_offset; +}; + +struct gx_formatter_ops { + struct gx_stream *(*get_stream)(struct snd_soc_dapm_widget *w); + void (*enable)(struct regmap *map); + void (*disable)(struct regmap *map); + int (*prepare)(struct regmap *map, + const struct gx_formatter_hw *quirks, + struct gx_stream *ts); +}; + +struct gx_formatter_driver { + const struct snd_soc_component_driver *component_drv; + const struct regmap_config *regmap_cfg; + const struct gx_formatter_ops *ops; + const struct gx_formatter_hw *quirks; +}; + +int gx_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event); +int gx_formatter_probe(struct platform_device *pdev); + +int gx_formatter_create(struct device *dev, + struct snd_soc_dapm_widget *w, + const struct gx_formatter_driver *drv, + struct regmap *regmap); + +/* + * Formatter data is already freed when the associated device is removed, + * so we just need to remove the pointer from the widget. + */ +static inline void gx_formatter_free(struct snd_soc_dapm_widget *w) +{ + w->priv = NULL; +} + +#endif /* _MESON_GX_FORMATTER_H */ diff --git a/sound/soc/meson/gx-interface.h b/sound/soc/meson/gx-interface.h new file mode 100644 index 000000000000..2a6207e393e8 --- /dev/null +++ b/sound/soc/meson/gx-interface.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2026 Baylibre SAS. + * Author: Valerio Setti <vsetti@baylibre.com> + */ + +#ifndef _MESON_GX_INTERFACE_H +#define _MESON_GX_INTERFACE_H + +#include <linux/clk.h> +#include <linux/regmap.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +struct gx_iface { + struct clk *mclk; + unsigned long mclk_rate; + + /* format is common to all the DAIs of the iface */ + unsigned int fmt; +}; + +struct gx_stream { + struct gx_iface *iface; + struct list_head formatter_list; + struct mutex lock; + unsigned int channels; + unsigned int width; + unsigned int physical_width; + bool ready; + + /* For continuous clock tracking */ + bool clk_enabled; +}; + +struct gx_stream *gx_stream_alloc(struct gx_iface *iface); +void gx_stream_free(struct gx_stream *ts); +int gx_stream_start(struct gx_stream *ts); +void gx_stream_stop(struct gx_stream *ts); + +#endif /* _MESON_GX_INTERFACE_H */ diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index cdb759b466ad..f2796dc84fa5 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -50,25 +50,20 @@ int meson_card_reallocate_links(struct snd_soc_card *card, num_links * sizeof(*priv->card.dai_link), GFP_KERNEL | __GFP_ZERO); if (!links) - goto err_links; + return -ENOMEM; + + priv->card.dai_link = links; + priv->card.num_links = num_links; ldata = krealloc(priv->link_data, num_links * sizeof(*priv->link_data), GFP_KERNEL | __GFP_ZERO); + /* meson_card_clean_references() will free the links on this error path */ if (!ldata) - goto err_ldata; + return -ENOMEM; - priv->card.dai_link = links; priv->link_data = ldata; - priv->card.num_links = num_links; return 0; - -err_ldata: - kfree(links); -err_links: - dev_err(priv->card.dev, "failed to allocate links\n"); - return -ENOMEM; - } EXPORT_SYMBOL_GPL(meson_card_reallocate_links); @@ -76,6 +71,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, struct device_node *node, struct snd_soc_dai_link_component *dlc) { + struct device *dev = card->dev; int ret; if (!dlc || !node) @@ -83,7 +79,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, ret = snd_soc_of_get_dlc(node, NULL, dlc, 0); if (ret) - return dev_err_probe(card->dev, ret, "can't parse dai\n"); + return dev_err_probe(dev, ret, "can't parse dai\n"); return ret; } @@ -94,7 +90,8 @@ static int meson_card_set_link_name(struct snd_soc_card *card, struct device_node *node, const char *prefix) { - char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", + struct device *dev = card->dev; + char *name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", prefix, node->full_name); if (!name) return -ENOMEM; @@ -137,16 +134,17 @@ int meson_card_set_be_link(struct snd_soc_card *card, struct device_node *node) { struct snd_soc_dai_link_component *codec; + struct device *dev = card->dev; int ret, num_codecs; num_codecs = of_get_child_count(node); if (!num_codecs) { - dev_err(card->dev, "be link %s has no codec\n", + dev_err(dev, "be link %s has no codec\n", node->full_name); return -EINVAL; } - codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); + codec = devm_kcalloc(dev, num_codecs, sizeof(*codec), GFP_KERNEL); if (!codec) return -ENOMEM; @@ -163,7 +161,7 @@ int meson_card_set_be_link(struct snd_soc_card *card, ret = meson_card_set_link_name(card, link, node, "be"); if (ret) - dev_err(card->dev, "error setting %pOFn link name\n", node); + dev_err(dev, "error setting %pOFn link name\n", node); return ret; } @@ -194,12 +192,13 @@ EXPORT_SYMBOL_GPL(meson_card_set_fe_link); static int meson_card_add_links(struct snd_soc_card *card) { struct meson_card *priv = snd_soc_card_get_drvdata(card); - struct device_node *node = card->dev->of_node; + struct device *dev = card->dev; + struct device_node *node = dev->of_node; int num, i, ret; num = of_get_child_count(node); if (!num) { - dev_err(card->dev, "card has no links\n"); + dev_err(dev, "card has no links\n"); return -EINVAL; } @@ -224,8 +223,10 @@ static int meson_card_parse_of_optional(struct snd_soc_card *card, int (*func)(struct snd_soc_card *c, const char *p)) { + struct device *dev = card->dev; + /* If property is not provided, don't fail ... */ - if (!of_property_present(card->dev->of_node, propname)) + if (!of_property_present(dev->of_node, propname)) return 0; /* ... but do fail if it is provided and the parsing fails */ diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c index da1a93946d67..68b5323ca9c8 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -78,8 +78,14 @@ static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static const u64 t9015_dai_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J; + static const struct snd_soc_dai_ops t9015_dai_ops = { .set_fmt = t9015_dai_set_fmt, + .auto_selectable_formats = &t9015_dai_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver t9015_dai = { @@ -265,10 +271,8 @@ static int t9015_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); ret = device_reset(dev); - if (ret) { - dev_err(dev, "reset failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to reset device\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index a01a680ad4d7..a77cd516a1bf 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -826,12 +826,9 @@ static int mxs_saif_probe(struct platform_device *pdev) mxs_saif[saif->id] = saif; saif->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(saif->clk)) { - ret = PTR_ERR(saif->clk); - dev_err(&pdev->dev, "Cannot get the clock: %d\n", - ret); - return ret; - } + if (IS_ERR(saif->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(saif->clk), + "Cannot get the clock\n"); saif->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(saif->base)) @@ -844,10 +841,8 @@ static int mxs_saif_probe(struct platform_device *pdev) saif->dev = &pdev->dev; ret = devm_request_irq(&pdev->dev, irq, mxs_saif_irq, 0, dev_name(&pdev->dev), saif); - if (ret) { - dev_err(&pdev->dev, "failed to request irq\n"); + if (ret) return ret; - } platform_set_drvdata(pdev, saif); @@ -860,16 +855,12 @@ static int mxs_saif_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &mxs_saif_component, &mxs_saif_dai, 1); - if (ret) { - dev_err(&pdev->dev, "register DAI failed\n"); + if (ret) return ret; - } ret = mxs_pcm_platform_register(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "register PCM failed: %d\n", ret); + if (ret) return ret; - } return 0; } diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index f1c0e612313d..a253a48ca59c 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -155,8 +155,6 @@ static int mxs_sgtl5000_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); mxs_saif_put_mclk(0); return ret; } diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index f8054c1c59fa..3a0abcb0bfbd 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -769,13 +769,10 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai) goto err_priv; } - priv->extclk = devm_clk_get(dev, "extclk"); + priv->extclk = devm_clk_get_optional(dev, "extclk"); if (IS_ERR(priv->extclk)) { ret = PTR_ERR(priv->extclk); - if (ret == -EPROBE_DEFER) - goto err_priv; - - priv->extclk = NULL; + goto err_priv; } } else { priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); diff --git a/sound/soc/pxa/pxa2xx-ac97-lib.c b/sound/soc/pxa/pxa2xx-ac97-lib.c index d9c3935636da..d7f720431afb 100644 --- a/sound/soc/pxa/pxa2xx-ac97-lib.c +++ b/sound/soc/pxa/pxa2xx-ac97-lib.c @@ -316,10 +316,8 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) int irq; ac97_reg_base = devm_platform_ioremap_resource(dev, 0); - if (IS_ERR(ac97_reg_base)) { - dev_err(&dev->dev, "Missing MMIO resource\n"); + if (IS_ERR(ac97_reg_base)) return PTR_ERR(ac97_reg_base); - } if (cpu_is_pxa27x()) { /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index edc4611691f7..d231024206db 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -23,6 +23,161 @@ static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { SND_SOC_DAPM_SPK("DP7 Jack", NULL), }; +static struct device_node *qcom_snd_get_link_node(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_card *card = rtd->card; + struct of_phandle_args args; + int ret; + + if (!card->dev || !card->dev->of_node) + return NULL; + + for_each_available_child_of_node_scoped(card->dev->of_node, np) { + struct device_node *cpu_np __free(device_node) = + of_get_child_by_name(np, "cpu"); + + if (!cpu_np) + continue; + + ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, + &args); + if (ret) + continue; + + if (args.np == rtd->dai_link->cpus[0].of_node && + args.args_count == 1 && args.args[0] == cpu_dai->id) { + of_node_put(args.np); + return of_node_get(np); + } + + of_node_put(args.np); + } + + return NULL; +} + +static int qcom_snd_parse_tdm_slot(struct device_node *np, + struct qcom_snd_tdm_slot_cfg *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + + return snd_soc_of_parse_tdm_slot(np, &cfg->tx_mask, &cfg->rx_mask, + &cfg->slots, &cfg->slot_width); +} + +static int qcom_snd_normalize_tdm_slots(struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + unsigned int slots; + unsigned int slot_width; + + if (cpu_cfg->slots && codec_cfg->slots && cpu_cfg->slots != codec_cfg->slots) + return -EINVAL; + + if (cpu_cfg->slot_width && codec_cfg->slot_width && + cpu_cfg->slot_width != codec_cfg->slot_width) + return -EINVAL; + + slots = cpu_cfg->slots ?: codec_cfg->slots; + if (!slots) + return 0; + + slot_width = cpu_cfg->slot_width ?: codec_cfg->slot_width; + if (!slot_width) + return -EINVAL; + + cpu_cfg->slots = slots; + codec_cfg->slots = slots; + cpu_cfg->slot_width = slot_width; + codec_cfg->slot_width = slot_width; + + return 0; +} + +static int qcom_snd_parse_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + struct device_node *link_np __free(device_node) = qcom_snd_get_link_node(rtd); + int ret; + + if (!link_np) + return -EINVAL; + + struct device_node *cpu_np __free(device_node) = + of_get_child_by_name(link_np, "cpu"); + struct device_node *codec_np __free(device_node) = + of_get_child_by_name(link_np, "codec"); + if (!cpu_np || !codec_np) + return -EINVAL; + + ret = qcom_snd_parse_tdm_slot(cpu_np, cpu_cfg); + if (ret) + return ret; + + return qcom_snd_parse_tdm_slot(codec_np, codec_cfg); +} + +int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + int ret; + + ret = qcom_snd_parse_dai_tdm_slots(rtd, cpu_cfg, codec_cfg); + if (ret) + return ret; + + return qcom_snd_normalize_tdm_slots(cpu_cfg, codec_cfg); +} +EXPORT_SYMBOL_GPL(qcom_snd_get_dai_tdm_slots); + +int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, + const struct qcom_snd_tdm_slot_cfg *cpu_cfg, + const struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + int i; + int ret; + + if (!cpu_cfg->slots) + return 0; + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, cpu_cfg->tx_mask, cpu_cfg->rx_mask, + cpu_cfg->slots, cpu_cfg->slot_width); + if (ret) + return ret; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, + codec_cfg->tx_mask, + codec_cfg->rx_mask, + codec_cfg->slots, + codec_cfg->slot_width); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots_cfg); + +int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) +{ + struct qcom_snd_tdm_slot_cfg cpu_cfg; + struct qcom_snd_tdm_slot_cfg codec_cfg; + int ret; + + ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret == -EINVAL ? 0 : ret; + + return qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); +} +EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots); + int qcom_snd_parse_of(struct snd_soc_card *card) { struct device *dev = card->dev; @@ -91,7 +246,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = of_property_read_string(np, "link-name", &link->name); if (ret) { - dev_err(card->dev, "error getting codec dai_link name\n"); + dev_err(dev, "error getting codec dai_link name\n"); return ret; } @@ -109,7 +264,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); if (ret) { - dev_err_probe(card->dev, ret, + dev_err_probe(dev, ret, "%s: error getting cpu dai name\n", link->name); return ret; } @@ -126,7 +281,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) "sound-dai", 0); if (!link->platforms->of_node) { - dev_err(card->dev, "%s: platform dai not found\n", link->name); + dev_err(dev, "%s: platform dai not found\n", link->name); return -EINVAL; } } else { @@ -136,7 +291,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) if (codec) { ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { - dev_err_probe(card->dev, ret, + dev_err_probe(dev, ret, "%s: codec dai not found\n", link->name); return ret; } @@ -215,6 +370,7 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, } switch (cpu_dai->id) { + case LPI_MI2S_RX_0: case TX_CODEC_DMA_TX_0: case TX_CODEC_DMA_TX_1: case TX_CODEC_DMA_TX_2: diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index ee6662885593..c1deac109f24 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -7,9 +7,23 @@ #include <dt-bindings/sound/qcom,q6afe.h> #include <sound/soc.h> -#define LPASS_MAX_PORT (SENARY_MI2S_TX + 1) +#define LPASS_MAX_PORT (LPI_MI2S_TX_6 + 1) + +struct qcom_snd_tdm_slot_cfg { + unsigned int tx_mask; + unsigned int rx_mask; + unsigned int slots; + unsigned int slot_width; +}; int qcom_snd_parse_of(struct snd_soc_card *card); +int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg); +int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, + const struct qcom_snd_tdm_slot_cfg *cpu_cfg, + const struct qcom_snd_tdm_slot_cfg *codec_cfg); +int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd); int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_jack *jack, bool *jack_setup); int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index e6e9eb2e85aa..cce0ad31ff0c 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -152,6 +152,13 @@ struct apm_i2s_module_intf_cfg { #define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), 8) +struct apm_audio_if_module_intf_cfg { + struct apm_module_param_data param_data; + struct param_id_audio_if_intf_cfg cfg; +} __packed; + +#define APM_AUDIO_IF_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_audio_if_module_intf_cfg), 8) + struct apm_module_hw_ep_mf_cfg { struct apm_module_param_data param_data; struct param_id_hw_ep_mf mf; @@ -168,6 +175,13 @@ struct apm_module_frame_size_factor_cfg { #define APM_FS_CFG_PSIZE ALIGN(sizeof(struct apm_module_frame_size_factor_cfg), 8) +struct apm_module_hw_ep_frame_duration_cfg { + struct apm_module_param_data param_data; + struct param_id_hw_ep_frame_duration frame_duration; +} __packed; + +#define APM_HW_EP_FRAME_DURATION_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_frame_duration_cfg), 8) + struct apm_module_hw_ep_power_mode_cfg { struct apm_module_param_data param_data; struct param_id_hw_ep_power_mode_cfg power_mode; @@ -703,6 +717,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE; int size = ic_sz + ep_sz + fs_sz + pm_sz; void *p; + int i; struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); if (IS_ERR(pkt)) @@ -741,7 +756,12 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, intf_cfg->cfg.lpaif_type = module->hw_interface_type; intf_cfg->cfg.intf_index = module->hw_interface_idx; - intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1; + intf_cfg->cfg.active_channels_mask = 0; + /* Convert the physical channel mapping into a bit field */ + for (i = 0; i < AR_PCM_MAX_NUM_CHANNEL; i++) + if (cfg->channel_map[i]) + intf_cfg->cfg.active_channels_mask |= BIT(i); + p += ic_sz; pm_cfg = p; @@ -840,7 +860,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, uint32_t num_channels = cfg->num_channels; int payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) + APM_MODULE_PARAM_DATA_SIZE; - int i; + int i, j; void *p; struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); @@ -860,8 +880,12 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, media_format->sample_rate = cfg->sample_rate; media_format->bit_width = cfg->bit_width; media_format->num_channels = cfg->num_channels; - for (i = 0; i < num_channels; i++) - media_format->channel_mapping[i] = cfg->channel_map[i]; + /* Convert the physical mapping to a logical mapping of the channels */ + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { + if (!cfg->channel_map[i]) + continue; + media_format->channel_mapping[j++] = cfg->channel_map[i]; + } return q6apm_send_cmd_sync(graph->apm, pkt, 0); } @@ -1042,6 +1066,81 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, return q6apm_send_cmd_sync(graph->apm, pkt, 0); } +static int audioreach_audio_if_set_media_format(struct q6apm_graph *graph, + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) +{ + struct apm_module_hw_ep_frame_duration_cfg *fd_cfg; + struct apm_module_param_data *param_data; + struct apm_audio_if_module_intf_cfg *intf_cfg; + struct apm_module_hw_ep_mf_cfg *hw_cfg; + int ic_sz = APM_AUDIO_IF_INTF_CFG_PSIZE; + int ep_sz = APM_HW_EP_CFG_PSIZE; + int fd_sz = APM_HW_EP_FRAME_DURATION_PSIZE; + int size = ic_sz + ep_sz + fd_sz; + u32 slot_mask = cfg->slot_mask ? cfg->slot_mask : module->slot_mask; + u16 nslots_per_frame = cfg->nslots_per_frame ? + (u16)cfg->nslots_per_frame : module->nslots_per_frame; + u16 slot_width = cfg->slot_width ? (u16)cfg->slot_width : module->slot_width; + void *p; + + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + intf_cfg = p; + + param_data = &intf_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_AUDIO_IF_INTF_CFG; + param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE; + intf_cfg->cfg.qaif_type = module->qaif_type; + intf_cfg->cfg.intf_idx = (u16)module->hw_interface_idx; + intf_cfg->cfg.intf_mode = module->intf_mode; + intf_cfg->cfg.ctrl_data_out_enable = module->ctrl_data_out_enable; + intf_cfg->cfg.active_slot_mask = slot_mask; + intf_cfg->cfg.nslots_per_frame = nslots_per_frame; + intf_cfg->cfg.slot_width = slot_width; + intf_cfg->cfg.active_lane_mask = module->active_lane_mask; + intf_cfg->cfg.frame_sync_rate = module->frame_sync_rate; + intf_cfg->cfg.frame_sync_src = module->sync_src; + intf_cfg->cfg.frame_sync_mode = module->sync_mode; + intf_cfg->cfg.invert_frame_sync_pulse = module->ctrl_invert_sync_pulse; + intf_cfg->cfg.frame_sync_data_delay = module->ctrl_sync_data_delay; + intf_cfg->cfg.bit_clk_type = module->bit_clk_type; + intf_cfg->cfg.inv_int_bit_clk = module->inv_int_bit_clk; + intf_cfg->cfg.inv_ext_bit_clk = module->inv_ext_bit_clk; + + p += ic_sz; + hw_cfg = p; + param_data = &hw_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_HW_EP_MF_CFG; + param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE; + + hw_cfg->mf.sample_rate = cfg->sample_rate; + hw_cfg->mf.bit_width = cfg->bit_width; + hw_cfg->mf.num_channels = cfg->num_channels; + hw_cfg->mf.data_format = module->data_format; + + p += ep_sz; + fd_cfg = p; + param_data = &fd_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_HW_EP_FRAME_DURATION; + param_data->param_size = fd_sz - APM_MODULE_PARAM_DATA_SIZE; + fd_cfg->frame_duration.frame_duration_in_us = AUDIO_IF_FRAME_DURATION_US; + fd_cfg->frame_duration.allow_frame_duration_normalization = AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE; + fd_cfg->frame_duration.min_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MIN_US; + fd_cfg->frame_duration.max_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MAX_US; + + return q6apm_send_cmd_sync(graph->apm, pkt, 0); +} + static int audioreach_logging_set_media_format(struct q6apm_graph *graph, const struct audioreach_module *module) { @@ -1080,6 +1179,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, struct apm_pcm_module_media_fmt_cmd *cfg; struct apm_module_param_data *param_data; int payload_size; + int i, j; if (num_channels > 4) { dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); @@ -1113,7 +1213,12 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, media_cfg->num_channels = mcfg->num_channels; media_cfg->q_factor = mcfg->bit_width - 1; media_cfg->bits_per_sample = mcfg->bit_width; - memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); + /* Convert the physical mapping to a logical mapping of the channels */ + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < mcfg->num_channels; i++) { + if (!mcfg->channel_map[i]) + continue; + media_cfg->channel_mapping[j++] = mcfg->channel_map[i]; + } return q6apm_send_cmd_sync(graph->apm, pkt, 0); } @@ -1163,6 +1268,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, struct payload_media_fmt_pcm *cfg; struct media_format *header; int rc, payload_size; + int i, j; void *p; if (num_channels > 4) { @@ -1202,7 +1308,12 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, cfg->q_factor = mcfg->bit_width - 1; cfg->endianness = PCM_LITTLE_ENDIAN; cfg->num_channels = mcfg->num_channels; - memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); + /* Convert the physical mapping to a logical mapping of the channels */ + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { + if (!mcfg->channel_map[i]) + continue; + cfg->channel_mapping[j++] = mcfg->channel_map[i]; + } } else { rc = audioreach_set_compr_media_format(header, p, mcfg); if (rc) @@ -1279,7 +1390,7 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, struct apm_module_sp_vi_ex_mode_cfg *ex_cfg; int op_sz, cm_sz, ex_sz; struct apm_module_param_data *param_data; - int rc, i, payload_size; + int rc, i, payload_size, j; struct gpr_pkt *pkt; void *p; @@ -1320,14 +1431,19 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, param_data->param_size = cm_sz - APM_MODULE_PARAM_DATA_SIZE; cm_cfg->cfg.num_channels = num_channels * 2; - for (i = 0; i < num_channels; i++) { + /* Convert the physical mapping to a logical mapping of the channels */ + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < num_channels; i++) { + if (!mcfg->channel_map[i]) + continue; /* * Map speakers into Vsense and then Isense of each channel. * E.g. for PCM_CHANNEL_FL and PCM_CHANNEL_FR to: * [1, 2, 3, 4] */ - cm_cfg->cfg.channel_mapping[2 * i] = (mcfg->channel_map[i] - 1) * 2 + 1; - cm_cfg->cfg.channel_mapping[2 * i + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; + cm_cfg->cfg.channel_mapping[2 * j] = (mcfg->channel_map[i] - 1) * 2 + 1; + cm_cfg->cfg.channel_mapping[2 * j + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; + + ++j; } p += cm_sz; @@ -1411,6 +1527,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, if (!rc) rc = audioreach_module_enable(graph, module, true); break; + case MODULE_ID_AUDIO_IF_SOURCE: + case MODULE_ID_AUDIO_IF_SINK: + rc = audioreach_audio_if_set_media_format(graph, module, cfg); + break; default: rc = 0; diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 62a2fd79bbcb..35541b2d8c99 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -36,6 +36,8 @@ struct q6apm_graph; #define MODULE_ID_SPEAKER_PROTECTION 0x070010E2 #define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3 #define MODULE_ID_OPUS_DEC 0x07001174 +#define MODULE_ID_AUDIO_IF_SINK 0x0700117C +#define MODULE_ID_AUDIO_IF_SOURCE 0x0700117D #define APM_CMD_GET_SPF_STATE 0x01001021 #define APM_CMD_RSP_GET_SPF_STATE 0x02001007 @@ -544,6 +546,74 @@ struct param_id_i2s_intf_cfg { #define PORT_ID_I2S_OUPUT 1 #define I2S_STACK_SIZE 2048 +#define PARAM_ID_AUDIO_IF_INTF_CFG 0x08001B11 + +/* + * struct param_id_audio_if_intf_cfg - Audio interface configuration + * @qaif_type: Audio interface type (e.g. QAIF, QAIF_VA) + * @intf_idx: Interface instance index + * @intf_mode: Interface operating mode (TDM/PCM/I2S) + * @ctrl_data_out_enable: Enable sharing of data-out signal with other masters + * @active_slot_mask: Bitmask indicating active slots + * @nslots_per_frame: Number of slots per audio frame + * @slot_width: Width of each slot in bits + * @active_lane_mask: Bitmask of active data lanes + * @frame_sync_rate: Frame sync rate in Hz + * @frame_sync_src: Frame sync source selection + * @frame_sync_mode: Frame sync mode configuration + * @invert_frame_sync_pulse: Invert frame sync polarity when set + * @frame_sync_data_delay: Data delay from frame sync in bit clocks + * @bit_clk_type: Bit clock type (internal / external) + * @inv_int_bit_clk: Invert internal bit clock when set + * @inv_ext_bit_clk: Invert external bit clock when set + * + * This structure defines configuration parameters for the Qualcomm + * Audio Interface (QAIF) block. It is used to program interface + * characteristics such as slot configuration, clocking and frame + * synchronization behaviour. + */ +struct param_id_audio_if_intf_cfg { + uint16_t qaif_type; + uint16_t intf_idx; + uint16_t intf_mode; + uint16_t ctrl_data_out_enable; + uint32_t active_slot_mask; + uint16_t nslots_per_frame; + uint16_t slot_width; + uint32_t active_lane_mask; + uint32_t frame_sync_rate; + uint16_t frame_sync_src; + uint16_t frame_sync_mode; + uint16_t invert_frame_sync_pulse; + uint16_t frame_sync_data_delay; + uint16_t bit_clk_type; + uint8_t inv_int_bit_clk; + uint8_t inv_ext_bit_clk; +} __packed; + +#define PARAM_ID_HW_EP_FRAME_DURATION 0x08001B2F +#define AUDIO_IF_FRAME_DURATION_US 1000 +#define AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE 1 +#define AUDIO_IF_FRAME_DURATION_MIN_US 1 +#define AUDIO_IF_FRAME_DURATION_MAX_US 100000 + +/** + * struct param_id_hw_ep_frame_duration - Hardware endpoint frame duration + * @frame_duration_in_us: Frame duration in microseconds. + * @allow_frame_duration_normalization: Permit SPF to normalize frame duration. + * @min_normalized_frame_dur_us: Minimum normalized frame duration in microseconds. + * @max_normalized_frame_dur_us: Maximum normalized frame duration in microseconds. + * + * This structure configures the frame duration for the Audio IF hardware + * endpoint and, when enabled, the allowed normalization range. + */ +struct param_id_hw_ep_frame_duration { + uint32_t frame_duration_in_us; + uint32_t allow_frame_duration_normalization; + uint32_t min_normalized_frame_dur_us; + uint32_t max_normalized_frame_dur_us; +} __packed; + #define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154 struct param_id_display_port_intf_cfg { @@ -877,6 +947,23 @@ struct audioreach_module { uint32_t data_format; uint32_t hw_interface_type; + /* Audio IF module (TDM/PCM/I2S) */ + u32 slot_mask; + u32 active_lane_mask; + u32 frame_sync_rate; + u16 qaif_type; + u16 sync_src; + u16 ctrl_data_out_enable; + u16 nslots_per_frame; + u16 slot_width; + u16 intf_mode; + u16 sync_mode; + u16 ctrl_invert_sync_pulse; + u16 ctrl_sync_data_delay; + u16 bit_clk_type; + u8 inv_int_bit_clk; + u8 inv_ext_bit_clk; + /* PCM module specific */ uint32_t interleave_type; @@ -907,6 +994,9 @@ struct audioreach_module_config { u32 channel_allocation; u32 sd_line_mask; int fmt; + u32 slot_mask; + u16 nslots_per_frame; + u16 slot_width; struct snd_codec codec; u8 channel_map[AR_PCM_MAX_NUM_CHANNEL]; }; diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index a0d21034a626..920345609d2c 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -412,6 +412,8 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, case SENARY_MI2S_RX ... SENARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: + case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: rc = q6afe_i2s_port_prepare(dai_data->port[dai->id], &dai_data->port_config[dai->id].i2s_cfg); if (rc < 0) { @@ -665,6 +667,21 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { /* USB playback AFE port receives data for playback, hence use the RX port */ {"USB Playback", NULL, "USB_RX"}, + + {"LPI RX0 MI2S Playback", NULL, "LPI_MI2S_RX_0"}, + {"LPI_MI2S_TX_0", NULL, "LPI TX0 MI2S Capture"}, + {"LPI RX1 MI2S Playback", NULL, "LPI_MI2S_RX_1"}, + {"LPI_MI2S_TX_1", NULL, "LPI TX1 MI2S Capture"}, + {"LPI RX2 MI2S Playback", NULL, "LPI_MI2S_RX_2"}, + {"LPI_MI2S_TX_2", NULL, "LPI TX2 MI2S Capture"}, + {"LPI RX3 MI2S Playback", NULL, "LPI_MI2S_RX_3"}, + {"LPI_MI2S_TX_3", NULL, "LPI TX3 MI2S Capture"}, + {"LPI RX4 MI2S Playback", NULL, "LPI_MI2S_RX_4"}, + {"LPI_MI2S_TX_4", NULL, "LPI TX4 MI2S Capture"}, + {"LPI RX5 MI2S Playback", NULL, "LPI_MI2S_RX_5"}, + {"LPI_MI2S_TX_5", NULL, "LPI TX5 MI2S Capture"}, + {"LPI RX6 MI2S Playback", NULL, "LPI_MI2S_RX_6"}, + {"LPI_MI2S_TX_6", NULL, "LPI TX6 MI2S Capture"}, }; static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) @@ -1011,6 +1028,35 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("USB_RX", NULL, 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_0", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_0", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_1", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_1", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_2", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_2", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_3", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_3", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_4", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_4", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_5", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_5", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_6", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_6", NULL, + 0, SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_component_driver q6afe_dai_component = { @@ -1045,6 +1091,8 @@ static void of_q6afe_parse_dai_data(struct device *dev, case SENARY_MI2S_RX ... SENARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: + case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: priv = &data->priv[id]; ret = of_property_read_variable_u32_array(node, "qcom,sd-lines", diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 40237267fda0..1d68a80e8e0c 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -132,6 +132,20 @@ #define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 #define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 #define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 +#define AFE_PORT_ID_INT0_MI2S_RX 0x102e +#define AFE_PORT_ID_INT0_MI2S_TX 0x102f +#define AFE_PORT_ID_INT1_MI2S_RX 0x1030 +#define AFE_PORT_ID_INT1_MI2S_TX 0x1031 +#define AFE_PORT_ID_INT2_MI2S_RX 0x1032 +#define AFE_PORT_ID_INT2_MI2S_TX 0x1033 +#define AFE_PORT_ID_INT3_MI2S_RX 0x1034 +#define AFE_PORT_ID_INT3_MI2S_TX 0x1035 +#define AFE_PORT_ID_INT4_MI2S_RX 0x1036 +#define AFE_PORT_ID_INT4_MI2S_TX 0x1037 +#define AFE_PORT_ID_INT5_MI2S_RX 0x1038 +#define AFE_PORT_ID_INT5_MI2S_TX 0x1039 +#define AFE_PORT_ID_INT6_MI2S_RX 0x103a +#define AFE_PORT_ID_INT6_MI2S_TX 0x103b /* Start of the range of port IDs for TDM devices. */ #define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 @@ -931,6 +945,34 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7, RX_CODEC_DMA_RX_7, 1, 1}, [USB_RX] = { AFE_PORT_ID_USB_RX, USB_RX, 1, 1}, + [LPI_MI2S_RX_0] = { AFE_PORT_ID_INT0_MI2S_RX, + LPI_MI2S_RX_0, 1, 1}, + [LPI_MI2S_TX_0] = { AFE_PORT_ID_INT0_MI2S_TX, + LPI_MI2S_TX_0, 0, 1}, + [LPI_MI2S_RX_1] = { AFE_PORT_ID_INT1_MI2S_RX, + LPI_MI2S_RX_1, 1, 1}, + [LPI_MI2S_TX_1] = { AFE_PORT_ID_INT1_MI2S_TX, + LPI_MI2S_TX_1, 0, 1}, + [LPI_MI2S_RX_2] = { AFE_PORT_ID_INT2_MI2S_RX, + LPI_MI2S_RX_2, 1, 1}, + [LPI_MI2S_TX_2] = { AFE_PORT_ID_INT2_MI2S_TX, + LPI_MI2S_TX_2, 0, 1}, + [LPI_MI2S_RX_3] = { AFE_PORT_ID_INT3_MI2S_RX, + LPI_MI2S_RX_3, 1, 1}, + [LPI_MI2S_TX_3] = { AFE_PORT_ID_INT3_MI2S_TX, + LPI_MI2S_TX_3, 0, 1}, + [LPI_MI2S_RX_4] = { AFE_PORT_ID_INT4_MI2S_RX, + LPI_MI2S_RX_4, 1, 1}, + [LPI_MI2S_TX_4] = { AFE_PORT_ID_INT4_MI2S_TX, + LPI_MI2S_TX_4, 0, 1}, + [LPI_MI2S_RX_5] = { AFE_PORT_ID_INT5_MI2S_RX, + LPI_MI2S_RX_5, 1, 1}, + [LPI_MI2S_TX_5] = { AFE_PORT_ID_INT5_MI2S_TX, + LPI_MI2S_TX_5, 0, 1}, + [LPI_MI2S_RX_6] = { AFE_PORT_ID_INT6_MI2S_RX, + LPI_MI2S_RX_6, 1, 1}, + [LPI_MI2S_TX_6] = { AFE_PORT_ID_INT6_MI2S_TX, + LPI_MI2S_TX_6, 0, 1}, }; static void q6afe_port_free(struct kref *ref) @@ -1785,6 +1827,20 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_QUINARY_MI2S_TX: case AFE_PORT_ID_SENARY_MI2S_RX: case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_INT0_MI2S_RX: + case AFE_PORT_ID_INT0_MI2S_TX: + case AFE_PORT_ID_INT1_MI2S_RX: + case AFE_PORT_ID_INT1_MI2S_TX: + case AFE_PORT_ID_INT2_MI2S_RX: + case AFE_PORT_ID_INT2_MI2S_TX: + case AFE_PORT_ID_INT3_MI2S_RX: + case AFE_PORT_ID_INT3_MI2S_TX: + case AFE_PORT_ID_INT4_MI2S_RX: + case AFE_PORT_ID_INT4_MI2S_TX: + case AFE_PORT_ID_INT5_MI2S_RX: + case AFE_PORT_ID_INT5_MI2S_TX: + case AFE_PORT_ID_INT6_MI2S_RX: + case AFE_PORT_ID_INT6_MI2S_TX: cfg_type = AFE_PARAM_ID_I2S_CONFIG; break; case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 006b283484d9..e204fd59e512 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -2,10 +2,12 @@ // Copyright (c) 2021, Linaro Limited #include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h> +#include <linux/clk.h> #include <linux/err.h> #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <sound/pcm.h> @@ -15,15 +17,54 @@ #include "q6dsp-common.h" #include "audioreach.h" #include "q6apm.h" +#include "q6prm.h" #define AUDIOREACH_BE_PCM_BASE 16 +struct q6apm_dai_priv_data { + struct clk *mclk; + struct clk *bclk; + bool mclk_enabled, bclk_enabled; +}; + struct q6apm_lpass_dai_data { struct q6apm_graph *graph[APM_PORT_MAX]; bool is_port_started[APM_PORT_MAX]; struct audioreach_module_config module_config[APM_PORT_MAX]; + struct q6apm_dai_priv_data priv[APM_PORT_MAX]; }; +static void q6apm_lpass_dai_disable_clocks(struct q6apm_lpass_dai_data *dai_data, int id) +{ + if (dai_data->priv[id].mclk_enabled) { + clk_disable_unprepare(dai_data->priv[id].mclk); + dai_data->priv[id].mclk_enabled = false; + } + + if (dai_data->priv[id].bclk_enabled) { + clk_disable_unprepare(dai_data->priv[id].bclk); + dai_data->priv[id].bclk_enabled = false; + } +} + +static void q6apm_lpass_dai_put_clocks(struct q6apm_lpass_dai_data *dai_data) +{ + int i; + + for (i = 0; i < APM_PORT_MAX; i++) { + q6apm_lpass_dai_disable_clocks(dai_data, i); + + if (dai_data->priv[i].mclk) { + clk_put(dai_data->priv[i].mclk); + dai_data->priv[i].mclk = NULL; + } + if (dai_data->priv[i].bclk) { + clk_put(dai_data->priv[i].bclk); + dai_data->priv[i].bclk = NULL; + } + } +} + static int q6dma_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_ch_mask, @@ -251,6 +292,62 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s return 0; } +static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return q6apm_lpass_dai_startup(substream, dai); +} + +static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + + q6apm_lpass_dai_shutdown(substream, dai); + q6apm_lpass_dai_disable_clocks(dai_data, dai->id); +} + +static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct clk *sysclk = NULL; + bool *enabled = NULL; + int ret = 0; + + switch (clk_id) { + case LPAIF_MI2S_MCLK: + sysclk = dai_data->priv[dai->id].mclk; + enabled = &dai_data->priv[dai->id].mclk_enabled; + break; + case LPAIF_MI2S_BCLK: + sysclk = dai_data->priv[dai->id].bclk; + enabled = &dai_data->priv[dai->id].bclk_enabled; + break; + default: + return -EINVAL; + } + + if (sysclk) { + ret = clk_set_rate(sysclk, freq); + if (ret) { + dev_err(dai->dev, "Error, Unable to set rate (%d) for sysclk %d\n", + freq, clk_id); + return ret; + } + + if (*enabled) + return 0; + + ret = clk_prepare_enable(sysclk); + if (ret) { + dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); + return ret; + } + + *enabled = true; + } + + return ret; +} + static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -261,6 +358,53 @@ static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; + unsigned int cap_mask, slot_mask; + + if (slot_width != 16 && slot_width != 32) { + dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width); + return -EINVAL; + } + + switch (slots) { + case 2: + case 4: + case 8: + case 16: + cap_mask = GENMASK(slots - 1, 0); + break; + default: + dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); + return -EINVAL; + } + + switch (dai->id) { + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask; + if (slot_mask & ~cap_mask) { + dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n", + __func__, slot_mask, slots); + return -EINVAL; + } + + cfg->nslots_per_frame = slots; + cfg->slot_width = slot_width; + cfg->slot_mask = slot_mask; + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id); + return -EINVAL; + } + + return 0; +} + static const struct snd_soc_dai_ops q6dma_ops = { .prepare = q6apm_lpass_dai_prepare, .startup = q6apm_lpass_dai_startup, @@ -272,11 +416,12 @@ static const struct snd_soc_dai_ops q6dma_ops = { static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6apm_lpass_dai_prepare, - .startup = q6apm_lpass_dai_startup, - .shutdown = q6apm_lpass_dai_shutdown, + .startup = q6i2s_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, + .set_sysclk = q6i2s_set_sysclk, .trigger = q6apm_lpass_dai_trigger, }; @@ -289,6 +434,17 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .trigger = q6apm_lpass_dai_trigger, }; +static const struct snd_soc_dai_ops q6tdm_ops = { + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, + .set_tdm_slot = q6tdm_set_tdm_slot, + .hw_params = q6dma_hw_params, + .set_fmt = q6i2s_set_fmt, + .set_sysclk = q6i2s_set_sysclk, + .trigger = q6apm_lpass_dai_trigger, +}; + static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .name = "q6apm-be-dai-component", .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, @@ -297,6 +453,65 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .remove_order = SND_SOC_COMP_ORDER_FIRST, }; +static int of_q6apm_parse_dai_data(struct device *dev, + struct q6apm_lpass_dai_data *data) +{ + int ret; + + for_each_child_of_node_scoped(dev->of_node, node) { + struct q6apm_dai_priv_data *priv; + int id; + + ret = of_property_read_u32(node, "reg", &id); + if (ret || id < 0 || id >= APM_PORT_MAX) { + dev_err(dev, "valid dai id not found:%d\n", ret); + continue; + } + + switch (id) { + /* MI2S specific properties */ + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + priv = &data->priv[id]; + priv->mclk = of_clk_get_by_name(node, "mclk"); + if (IS_ERR(priv->mclk)) { + int err = PTR_ERR(priv->mclk); + + priv->mclk = NULL; + if (err == -EPROBE_DEFER) { + q6apm_lpass_dai_put_clocks(data); + return dev_err_probe(dev, err, + "unable to get mi2s mclk\n"); + } + } + + priv->bclk = of_clk_get_by_name(node, "bclk"); + if (IS_ERR(priv->bclk)) { + int err = PTR_ERR(priv->bclk); + + priv->bclk = NULL; + if (err == -EPROBE_DEFER) { + q6apm_lpass_dai_put_clocks(data); + return dev_err_probe(dev, err, + "unable to get mi2s bclk\n"); + } + } + break; + default: + break; + } + } + + return 0; +} + +static void q6apm_lpass_dai_clocks_action(void *data) +{ + q6apm_lpass_dai_put_clocks(data); +} + static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) { struct q6dsp_audio_port_dai_driver_config cfg; @@ -304,17 +519,26 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) struct snd_soc_dai_driver *dais; struct device *dev = &pdev->dev; int num_dais; + int ret; dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); if (!dai_data) return -ENOMEM; dev_set_drvdata(dev, dai_data); + ret = of_q6apm_parse_dai_data(dev, dai_data); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, q6apm_lpass_dai_clocks_action, dai_data); + if (ret) + return ret; memset(&cfg, 0, sizeof(cfg)); cfg.q6i2s_ops = &q6i2s_ops; cfg.q6dma_ops = &q6dma_ops; cfg.q6hdmi_ops = &q6hdmi_ops; + cfg.q6tdm_ops = &q6tdm_ops; dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 641d6d243229..f167b9dae3fa 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -802,14 +802,17 @@ EXPORT_SYMBOL_GPL(q6apm_graph_prepare); int q6apm_graph_start(struct q6apm_graph *graph) { struct audioreach_graph *ar_graph = graph->ar_graph; - int ret = 0; + int ret; - if (ar_graph->start_count == 0) + if (ar_graph->start_count == 0) { ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START); + if (ret) + return ret; + } ar_graph->start_count++; - return ret; + return 0; } EXPORT_SYMBOL_GPL(q6apm_graph_start); @@ -817,6 +820,9 @@ int q6apm_graph_stop(struct q6apm_graph *graph) { struct audioreach_graph *ar_graph = graph->ar_graph; + if (ar_graph->start_count == 0) + return 0; + if (--ar_graph->start_count > 0) return 0; diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c index 03838582aead..ab7d20580638 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c @@ -12,7 +12,7 @@ #include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h> #include "q6dsp-lpass-clocks.h" -#define Q6DSP_MAX_CLK_ID 104 +#define Q6DSP_MAX_CLK_ID 105 #define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0 diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index e5cd82f77b55..c3d8116ad503 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -553,11 +553,15 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { Q6AFE_MI2S_RX_DAI("LPI RX2", LPI_MI2S_RX_2), Q6AFE_MI2S_RX_DAI("LPI RX3", LPI_MI2S_RX_3), Q6AFE_MI2S_RX_DAI("LPI RX4", LPI_MI2S_RX_4), + Q6AFE_MI2S_RX_DAI("LPI RX5", LPI_MI2S_RX_5), + Q6AFE_MI2S_RX_DAI("LPI RX6", LPI_MI2S_RX_6), Q6AFE_MI2S_TX_DAI("LPI TX0", LPI_MI2S_TX_0), Q6AFE_MI2S_TX_DAI("LPI TX1", LPI_MI2S_TX_1), Q6AFE_MI2S_TX_DAI("LPI TX2", LPI_MI2S_TX_2), Q6AFE_MI2S_TX_DAI("LPI TX3", LPI_MI2S_TX_3), Q6AFE_MI2S_TX_DAI("LPI TX4", LPI_MI2S_TX_4), + Q6AFE_MI2S_TX_DAI("LPI TX5", LPI_MI2S_TX_5), + Q6AFE_MI2S_TX_DAI("LPI TX6", LPI_MI2S_TX_6), Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0), Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1), Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2), @@ -712,6 +716,7 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: + case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops; break; case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c index 4c574b48ab00..f613e2aee75e 100644 --- a/sound/soc/qcom/qdsp6/q6prm-clocks.c +++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c @@ -42,6 +42,11 @@ static const struct q6dsp_clk_init q6prm_clks[] = { Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_1), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_2), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_3), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_4), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_5), Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK), Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK), @@ -59,10 +64,40 @@ static const struct q6dsp_clk_init q6prm_clks[] = { Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_MCLK), Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK), Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_EBIT), Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS, "LPASS_HW_MACRO"), Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC, "LPASS_HW_DCODEC"), + Q6DSP_VOTE_CLK(LPASS_HW_LPR_VOTE, Q6PRM_HW_LPR_VOTE, + "LPASS_HW_LPR_VOTE"), }; static const struct q6dsp_clk_desc q6dsp_clk_q6prm __maybe_unused = { diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c index 04892fb4423f..1f3ce4cc0837 100644 --- a/sound/soc/qcom/qdsp6/q6prm.c +++ b/sound/soc/qcom/qdsp6/q6prm.c @@ -31,10 +31,16 @@ struct q6prm { #define PARAM_ID_RSC_HW_CORE 0x08001032 #define PARAM_ID_RSC_LPASS_CORE 0x0800102B #define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C +#define PARAM_ID_RSC_CPU_LPR 0x08001A6E + +#define LPR_CPU_SS_SLEEP_DISABLE 0x1 struct prm_cmd_request_hw_core { struct apm_module_param_data param_data; - uint32_t hw_clk_id; + union { + u32 hw_clk_id; + u32 lpr_state; + }; } __packed; struct prm_cmd_request_rsc { @@ -62,6 +68,7 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool struct prm_cmd_request_hw_core *req; gpr_device_t *gdev = prm->gdev; uint32_t opcode, rsp_opcode; + bool lpr_req = (hw_block_id == Q6PRM_HW_LPR_VOTE); if (enable) { opcode = PRM_CMD_REQUEST_HW_RSC; @@ -82,10 +89,13 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool param_data->module_instance_id = GPR_PRM_MODULE_IID; param_data->error_code = 0; - param_data->param_id = PARAM_ID_RSC_HW_CORE; + param_data->param_id = lpr_req ? PARAM_ID_RSC_CPU_LPR : PARAM_ID_RSC_HW_CORE; param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; - req->hw_clk_id = hw_block_id; + if (lpr_req) + req->lpr_state = LPR_CPU_SS_SLEEP_DISABLE; + else + req->hw_clk_id = hw_block_id; return q6prm_send_cmd_sync(prm, pkt, rsp_opcode); } diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index a988a32086fe..cca77cd92bc1 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -3,6 +3,9 @@ #ifndef __Q6PRM_H__ #define __Q6PRM_H__ +#define LPAIF_MI2S_MCLK 1 +#define LPAIF_MI2S_BCLK 2 + /* Clock ID for Primary I2S IBIT */ #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 /* Clock ID for Primary I2S EBIT */ @@ -52,6 +55,17 @@ /* Clock ID for QUINARY MI2S OSR CLK */ #define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 +/* Clock ID for MCLK1 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300 +/* Clock ID for MCLK2 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301 +/* Clock ID for MCLK3 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302 +/* Clock ID for MCLK4 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303 +/* Clock ID for MCLK5 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304 + #define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305 #define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306 @@ -83,10 +97,40 @@ /* Clock ID for RX CORE MCLK2 2X MCLK */ #define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_IBIT 0x500 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_EBIT 0x501 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_IBIT 0x502 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_EBIT 0x503 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_IBIT 0x504 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_EBIT 0x505 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_IBIT 0x506 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_EBIT 0x507 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_IBIT 0x508 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_EBIT 0x509 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_IBIT 0x50A +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_EBIT 0x50B +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_IBIT 0x50C +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_EBIT 0x50D +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_IBIT 0x50E +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_EBIT 0x50F +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_IBIT 0x510 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_EBIT 0x511 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_IBIT 0x512 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_EBIT 0x513 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_IBIT 0x514 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_EBIT 0x515 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_IBIT 0x516 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_EBIT 0x517 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_IBIT 0x518 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_EBIT 0x519 +#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 0x550 +#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 0x551 + #define Q6PRM_LPASS_CLK_SRC_INTERNAL 1 #define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0 #define Q6PRM_HW_CORE_ID_LPASS 1 #define Q6PRM_HW_CORE_ID_DCODEC 2 +#define Q6PRM_HW_LPR_VOTE 3 int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root, unsigned int freq); diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index 7386226046fa..d076c99f197f 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -127,7 +127,14 @@ { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \ { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \ { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \ - { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"} + { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"}, \ + { mix_name, "LPI_MI2S_TX_0", "LPI_MI2S_TX_0" }, \ + { mix_name, "LPI_MI2S_TX_1", "LPI_MI2S_TX_1" }, \ + { mix_name, "LPI_MI2S_TX_2", "LPI_MI2S_TX_2" }, \ + { mix_name, "LPI_MI2S_TX_3", "LPI_MI2S_TX_3" }, \ + { mix_name, "LPI_MI2S_TX_4", "LPI_MI2S_TX_4" }, \ + { mix_name, "LPI_MI2S_TX_5", "LPI_MI2S_TX_5" }, \ + { mix_name, "LPI_MI2S_TX_6", "LPI_MI2S_TX_6" } #define Q6ROUTING_TX_MIXERS(id) \ SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ @@ -321,6 +328,27 @@ msm_routing_put_audio_mixer), \ SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \ id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_0", LPI_MI2S_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_1", LPI_MI2S_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_2", LPI_MI2S_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_3", LPI_MI2S_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_4", LPI_MI2S_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_5", LPI_MI2S_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_6", LPI_MI2S_TX_6, \ + id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), struct session_data { @@ -709,6 +737,26 @@ static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = { static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = { Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) }; +static const struct snd_kcontrol_new lpi_mi2s_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_0) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_1) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_2) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_3) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_4) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_5) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_6) }; static const struct snd_kcontrol_new mmul1_mixer_controls[] = { Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; @@ -938,6 +986,27 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_MIXER("USB_RX Audio Mixer", SND_SOC_NOPM, 0, 0, usb_rx_mixer_controls, ARRAY_SIZE(usb_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_0_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_1_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_2_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_3_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_4_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_5_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_6_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_6_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, @@ -1031,6 +1100,13 @@ static const struct snd_soc_dapm_route intercon[] = { Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"), Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"), Q6ROUTING_RX_DAPM_ROUTE("USB_RX Audio Mixer", "USB_RX"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_0 Audio Mixer", "LPI_MI2S_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_1 Audio Mixer", "LPI_MI2S_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_2 Audio Mixer", "LPI_MI2S_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_3 Audio Mixer", "LPI_MI2S_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_4 Audio Mixer", "LPI_MI2S_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_5 Audio Mixer", "LPI_MI2S_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_6 Audio Mixer", "LPI_MI2S_RX_6"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 1f69fba6de26..faafbc5c0ad0 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -753,6 +753,103 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, return 0; } +static int audioreach_widget_audio_if_module_load(struct audioreach_module *mod, + const struct snd_soc_tplg_vendor_array *mod_array) +{ + const struct snd_soc_tplg_vendor_value_elem *mod_elem; + int tkn_count = 0; + u32 val; + + mod_elem = mod_array->value; + + while (tkn_count < le32_to_cpu(mod_array->num_elems)) { + val = le32_to_cpu(mod_elem->value); + switch (le32_to_cpu(mod_elem->token)) { + case AR_TKN_U32_MODULE_HW_IF_IDX: + mod->hw_interface_idx = val; + break; + case AR_TKN_U32_MODULE_FMT_DATA: + mod->data_format = val; + break; + case AR_TKN_U16_MODULE_SYNC_SRC: + if (val > U16_MAX) + return -EINVAL; + mod->sync_src = (u16)val; + break; + case AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: + if (val > U16_MAX) + return -EINVAL; + mod->ctrl_data_out_enable = (u16)val; + break; + case AR_TKN_U32_MODULE_SLOT_MASK: + mod->slot_mask = val; + break; + case AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: + if (val > U16_MAX) + return -EINVAL; + mod->nslots_per_frame = (u16)val; + break; + case AR_TKN_U16_MODULE_SLOT_WIDTH: + if (val > U16_MAX) + return -EINVAL; + mod->slot_width = (u16)val; + break; + case AR_TKN_U16_MODULE_INTF_MODE: + if (val > U16_MAX) + return -EINVAL; + mod->intf_mode = (u16)val; + break; + case AR_TKN_U16_MODULE_SYNC_MODE: + if (val > U16_MAX) + return -EINVAL; + mod->sync_mode = (u16)val; + break; + case AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: + if (val > U16_MAX) + return -EINVAL; + mod->ctrl_invert_sync_pulse = (u16)val; + break; + case AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: + if (val > U16_MAX) + return -EINVAL; + mod->ctrl_sync_data_delay = (u16)val; + break; + case AR_TKN_U16_MODULE_QAIF_TYPE: + if (val > U16_MAX) + return -EINVAL; + mod->qaif_type = (u16)val; + break; + case AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: + mod->active_lane_mask = val; + break; + case AR_TKN_U32_MODULE_FRAME_SYNC_RATE: + mod->frame_sync_rate = val; + break; + case AR_TKN_U16_MODULE_BIT_CLK_TYPE: + if (val > U16_MAX) + return -EINVAL; + mod->bit_clk_type = (u16)val; + break; + case AR_TKN_U8_MODULE_INV_INT_BIT_CLK: + if (val > U8_MAX) + return -EINVAL; + mod->inv_int_bit_clk = (u8)val; + break; + case AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: + if (val > U8_MAX) + return -EINVAL; + mod->inv_ext_bit_clk = (u8)val; + break; + default: + break; + } + tkn_count++; + mod_elem++; + } + + return 0; +} + static int audioreach_widget_dp_module_load(struct audioreach_module *mod, const struct snd_soc_tplg_vendor_array *mod_array) { @@ -806,6 +903,12 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, case MODULE_ID_I2S_SOURCE: audioreach_widget_i2s_module_load(mod, mod_array); break; + case MODULE_ID_AUDIO_IF_SINK: + case MODULE_ID_AUDIO_IF_SOURCE: + ret = audioreach_widget_audio_if_module_load(mod, mod_array); + if (ret) + return ret; + break; case MODULE_ID_DISPLAY_PORT_SINK: audioreach_widget_dp_module_load(mod, mod_array); break; @@ -1313,7 +1416,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) { struct snd_soc_card *card = component->card; struct device *dev = component->dev; - const struct firmware *fw; int ret; /* Inline with Qualcomm UCM configs and linux-firmware path */ @@ -1323,6 +1425,7 @@ int audioreach_tplg_init(struct snd_soc_component *component) if (!tplg_fw_name) return -ENOMEM; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, tplg_fw_name, dev); if (ret < 0) { dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret); @@ -1335,8 +1438,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) dev_err(dev, "tplg component load failed: %d\n", ret); } - release_firmware(fw); - return ret; } EXPORT_SYMBOL_GPL(audioreach_tplg_init); diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 98b15a527e37..1322601659d4 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -12,17 +12,157 @@ #include <sound/jack.h> #include <linux/input-event-codes.h> #include "qdsp6/q6afe.h" +#include "qdsp6/q6apm.h" +#include "qdsp6/q6prm.h" +#include "qdsp6/q6dsp-common.h" #include "common.h" #include "sdw.h" +#define I2S_MCLKFS 256 + +#define I2S_MCLK_RATE(rate) \ + ((rate) * (I2S_MCLKFS)) +#define I2S_BIT_RATE(rate, channels, format) \ + ((rate) * (channels) * (format)) + +static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("DP0 Jack", NULL), + SND_SOC_DAPM_SPK("DP1 Jack", NULL), + SND_SOC_DAPM_SPK("DP2 Jack", NULL), + SND_SOC_DAPM_SPK("DP3 Jack", NULL), + SND_SOC_DAPM_SPK("DP4 Jack", NULL), + SND_SOC_DAPM_SPK("DP5 Jack", NULL), + SND_SOC_DAPM_SPK("DP6 Jack", NULL), + SND_SOC_DAPM_SPK("DP7 Jack", NULL), +}; + +static const struct snd_kcontrol_new max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Headset Mic12"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic56"), + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Receiver"), + SOC_DAPM_PIN_SWITCH("Int Mic"), +}; + +static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic12", NULL), + SND_SOC_DAPM_MIC("Headset Mic56", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SPK("Receiver", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +struct qcom_snd_soc_common { + const char *driver_name; + const struct snd_soc_dapm_widget *dapm_widgets; + int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + const struct snd_kcontrol_new *controls; + int num_controls; + unsigned int codec_dai_fmt; + bool codec_sysclk_set; + bool mi2s_mclk_enable; + bool mi2s_bclk_enable; + bool wcd_jack; + int (*snd_prepare)(struct snd_pcm_substream *substream); +}; + struct sc8280xp_snd_data { bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; + const struct qcom_snd_soc_common *priv; bool jack_setup; }; +static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) +{ + int rate = params_rate(params); + + switch (rate) { + case 11025: + case 44100: + case 88200: + return I2S_MCLK_RATE(44100); + default: + break; + } + + return I2S_MCLK_RATE(rate); +} + +static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) +{ + return I2S_BIT_RATE(params_rate(params), + params_channels(params), + snd_pcm_format_width(params_format(params))); +} + +static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + struct qcom_snd_tdm_slot_cfg cpu_cfg; + struct qcom_snd_tdm_slot_cfg codec_cfg; + unsigned int bclk_freq; + int ret; + int i; + + ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret == -EINVAL ? 0 : ret; + + if (!cpu_cfg.slots) + return 0; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); + if (ret) + return ret; + + ret = qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret; + + bclk_freq = snd_soc_tdm_params_to_bclk(params, cpu_cfg.slot_width, cpu_cfg.slots, 1); + if (!bclk_freq) + return -EINVAL; + + if (data->priv->mi2s_bclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", + __func__, ret); + return ret; + } + } + + if (data->priv->codec_sysclk_set) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", + __func__, codec_dai->name, ret); + return ret; + } + } + } + + return 0; +} + static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) { struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); @@ -32,10 +172,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) int dp_pcm_id = 0; switch (cpu_dai->id) { - case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: - case QUINARY_MI2S_RX...QUINARY_MI2S_TX: - snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); - break; case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: /* @@ -64,7 +200,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) if (dp_jack) return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); - return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + if (data->priv->wcd_jack) + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + + return 0; } static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -96,12 +235,118 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + int mclk_freq = sc8280xp_get_mclk_freq(params); + int bclk_freq = sc8280xp_get_bclk_freq(params); + int ret; + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); + if (ret && ret != -ENOTSUPP) + return ret; + + if (data->priv->codec_dai_fmt) { + ret = snd_soc_dai_set_fmt(codec_dai, + data->priv->codec_dai_fmt); + if (ret && ret != -ENOTSUPP) + return ret; + } + + if (data->priv->mi2s_mclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_MCLK, mclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } + + if (data->priv->mi2s_bclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_BCLK, bclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } + + if (data->priv->codec_sysclk_set) { + ret = snd_soc_dai_set_sysclk(codec_dai, + 0, mclk_freq, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } + break; + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + return sc8280xp_tdm_hw_params(substream, params); + default: + break; + } + + return 0; +} + +/* + * WSA and WSA2 are handled as a single interface with the + * following channels mask: + * __________________________________________________ + * | Bits | 3 | 2 | 1 | 0 | + * --------------------------------------------------- + * | Line | WSA2 Ch2 | WSA2 Ch1 | WSA Ch2 | WSA Ch1 | + * --------------------------------------------------- + * + * The Ayaneo Pocket S2 speakers are connected only to + * the WSA2 interface and the WSA interface is not enabled. + * + * Set the channel mapping on the WSA2 channels only. + */ +static const unsigned int ayaneo_ps2_channels_mapping[] = { + 0, /* WSA Ch1 */ + 0, /* WSA Ch2 */ + PCM_CHANNEL_FL, /* WSA2 Ch1 */ + PCM_CHANNEL_FR /* WSA2 Ch2 */ +}; + +static int ayaneo_ps2_snd_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + unsigned int channels = substream->runtime->channels; + + if (cpu_dai->id != WSA_CODEC_DMA_RX_0) + return 0; + + if (channels != 2) + return -EINVAL; + + return snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + ARRAY_SIZE(ayaneo_ps2_channels_mapping), + ayaneo_ps2_channels_mapping); +} + static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + if (data->priv->snd_prepare) { + int ret; + + ret = data->priv->snd_prepare(substream); + if (ret) + return ret; + } + return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); } @@ -117,6 +362,7 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) static const struct snd_soc_ops sc8280xp_be_ops = { .startup = qcom_snd_sdw_startup, .shutdown = qcom_snd_sdw_shutdown, + .hw_params = sc8280xp_snd_hw_params, .hw_free = sc8280xp_snd_hw_free, .prepare = sc8280xp_snd_prepare, }; @@ -145,38 +391,182 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; - card->owner = THIS_MODULE; + /* Allocate the private data */ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + data->priv = of_device_get_match_data(dev); + if (!data->priv) + return -ENODEV; + + card->owner = THIS_MODULE; card->dev = dev; dev_set_drvdata(dev, card); snd_soc_card_set_drvdata(card, data); + card->dapm_widgets = data->priv->dapm_widgets; + card->num_dapm_widgets = data->priv->num_dapm_widgets; + card->dapm_routes = data->priv->dapm_routes; + card->num_dapm_routes = data->priv->num_dapm_routes; + card->controls = data->priv->controls; + card->num_controls = data->priv->num_controls; + ret = qcom_snd_parse_of(card); if (ret) return ret; - card->driver_name = of_device_get_match_data(dev); + card->driver_name = data->priv->driver_name; sc8280xp_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } +static struct qcom_snd_soc_common ayaneo_ps2_priv_data = { + .driver_name = "ayaneo-ps2", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .snd_prepare = ayaneo_ps2_snd_prepare, + .wcd_jack = true, +}; + +static const struct qcom_snd_soc_common eliza_priv_data = { + .driver_name = "eliza", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct qcom_snd_soc_common hawi_priv_data = { + .driver_name = "hawi", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .codec_sysclk_set = true, + .mi2s_bclk_enable = true, + .wcd_jack = true, +}; + +static const struct qcom_snd_soc_common kaanapali_priv_data = { + .driver_name = "kaanapali", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct qcom_snd_soc_common qcs9100_priv_data = { + .driver_name = "sa8775p", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static const struct qcom_snd_soc_common qcs615_priv_data = { + .driver_name = "qcs615", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .codec_sysclk_set = true, +}; + +static const struct qcom_snd_soc_common qcm6490_priv_data = { + .driver_name = "qcm6490", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct qcom_snd_soc_common qcs6490_priv_data = { + .driver_name = "qcs6490", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct qcom_snd_soc_common qcs8275_priv_data = { + .driver_name = "qcs8300", + .dapm_widgets = max98090_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98090_dapm_widgets), + .controls = max98090_controls, + .num_controls = ARRAY_SIZE(max98090_controls), + .codec_sysclk_set = true, + .codec_dai_fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BC_FC, +}; + +static const struct qcom_snd_soc_common sc8280xp_priv_data = { + .driver_name = "sc8280xp", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct qcom_snd_soc_common sm8450_priv_data = { + .driver_name = "sm8450", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, + /* I2S Connected to HDMI */ + .mi2s_mclk_enable = true, + .mi2s_bclk_enable = true, + .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S, +}; + +static const struct qcom_snd_soc_common sm8475_priv_data = { + .driver_name = "sm8475", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct qcom_snd_soc_common sm8550_priv_data = { + .driver_name = "sm8550", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, + /* I2S Connected to HDMI */ + .mi2s_mclk_enable = true, + .mi2s_bclk_enable = true, + .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S, +}; + +static const struct qcom_snd_soc_common sm8650_priv_data = { + .driver_name = "sm8650", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, + /* I2S Connected to HDMI */ + .mi2s_mclk_enable = true, + .mi2s_bclk_enable = true, + .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S, +}; + +static const struct qcom_snd_soc_common sm8750_priv_data = { + .driver_name = "sm8750", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + static const struct of_device_id snd_sc8280xp_dt_match[] = { - {.compatible = "qcom,eliza-sndcard", "eliza"}, - {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, - {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, - {.compatible = "qcom,qcs615-sndcard", "qcs615"}, - {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, - {.compatible = "qcom,qcs8275-sndcard", "qcs8300"}, - {.compatible = "qcom,qcs9075-sndcard", "sa8775p"}, - {.compatible = "qcom,qcs9100-sndcard", "sa8775p"}, - {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, - {.compatible = "qcom,sm8450-sndcard", "sm8450"}, - {.compatible = "qcom,sm8550-sndcard", "sm8550"}, - {.compatible = "qcom,sm8650-sndcard", "sm8650"}, - {.compatible = "qcom,sm8750-sndcard", "sm8750"}, + { .compatible = "ayaneo,pocket-s2-sndcard", .data = &ayaneo_ps2_priv_data }, + { .compatible = "qcom,eliza-sndcard", .data = &eliza_priv_data }, + { .compatible = "qcom,hawi-sndcard", .data = &hawi_priv_data }, + { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, + { .compatible = "qcom,maili-sndcard", .data = &hawi_priv_data }, + { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, + { .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data }, + { .compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data }, + { .compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data }, + { .compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data }, + { .compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data }, + { .compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data }, + { .compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data }, + { .compatible = "qcom,sm8475-sndcard", .data = &sm8475_priv_data }, + { .compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data }, + { .compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data }, + { .compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data }, {} }; diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 0ce9dff4dc52..6843ab8ba017 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -36,7 +36,6 @@ struct sdm845_snd_data { bool jack_setup; bool slim_port_setup; bool stream_prepared[AFE_PORT_MAX]; - struct snd_soc_card *card; uint32_t pri_mi2s_clk_count; uint32_t sec_mi2s_clk_count; uint32_t quat_tdm_clk_count; @@ -564,7 +563,6 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) if (ret) return ret; - data->card = card; snd_soc_card_set_drvdata(card, data); sdm845_add_ops(card); diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index f193d0ba63d0..76dc3a3f4a31 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -112,6 +112,22 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream) snd_soc_dai_set_fmt(cpu_dai, fmt); snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); break; + case LPI_MI2S_RX_0: + codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT, + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); + break; + case LPI_MI2S_TX_3: + codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT, + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); + snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); + break; default: break; } @@ -194,6 +210,7 @@ static const struct of_device_id snd_sm8250_dt_match[] = { { .compatible = "qcom,qrb2210-sndcard", .data = "qcm2290" }, { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, + { .compatible = "qcom,sdm660-sndcard", .data = "sdm660" }, { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" }, {} }; diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c index 1e0eda8c24c4..6b42fbfd27ca 100644 --- a/sound/soc/qcom/storm.c +++ b/sound/soc/qcom/storm.c @@ -64,21 +64,21 @@ static struct snd_soc_dai_link storm_dai_link = { SND_SOC_DAILINK_REG(hifi), }; -static int storm_parse_of(struct snd_soc_card *card) +static int storm_parse_of(struct device *dev) { - struct snd_soc_dai_link *dai_link = card->dai_link; - struct device_node *np = card->dev->of_node; + struct snd_soc_dai_link *dai_link = &storm_dai_link; + struct device_node *np = dev->of_node; dai_link->cpus->of_node = of_parse_phandle(np, "cpu", 0); if (!dai_link->cpus->of_node) { - dev_err(card->dev, "error getting cpu phandle\n"); + dev_err(dev, "error getting cpu phandle\n"); return -EINVAL; } dai_link->platforms->of_node = dai_link->cpus->of_node; dai_link->codecs->of_node = of_parse_phandle(np, "codec", 0); if (!dai_link->codecs->of_node) { - dev_err(card->dev, "error getting codec phandle\n"); + dev_err(dev, "error getting codec phandle\n"); return -EINVAL; } @@ -106,7 +106,7 @@ static int storm_platform_probe(struct platform_device *pdev) card->dai_link = &storm_dai_link; card->num_links = 1; - ret = storm_parse_of(card); + ret = storm_parse_of(&pdev->dev); if (ret) { dev_err(&pdev->dev, "error resolving dai links: %d\n", ret); return ret; diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c index ae86014c3819..23b7e63596d3 100644 --- a/sound/soc/renesas/fsi.c +++ b/sound/soc/renesas/fsi.c @@ -1979,8 +1979,8 @@ static const struct of_device_id fsi_of_match[] = { MODULE_DEVICE_TABLE(of, fsi_of_match); static const struct platform_device_id fsi_id_table[] = { - { "sh_fsi", (kernel_ulong_t)&fsi1_core }, - {}, + /* an array with no valid entry prevents matching on driver name */ + { } }; MODULE_DEVICE_TABLE(platform, fsi_id_table); @@ -1992,7 +1992,7 @@ static int fsi_probe(struct platform_device *pdev) const struct fsi_core *core; struct fsi_priv *fsi; struct resource *res; - unsigned int irq; + int irq; int ret; memset(&info, 0, sizeof(info)); @@ -2007,12 +2007,15 @@ static int fsi_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || (int)irq <= 0) { + if (!res) { dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); return -ENODEV; } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); if (!master) return -ENOMEM; diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c index 53efd1be5139..2a74d89ba39d 100644 --- a/sound/soc/renesas/rcar/adg.c +++ b/sound/soc/renesas/rcar/adg.c @@ -565,7 +565,7 @@ static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv, struct clk *clk; clk = clk_register_fixed_rate(dev, name, parent, 0, 0); - if (IS_ERR_OR_NULL(clk)) { + if (IS_ERR(clk)) { dev_err(dev, "create null clk error\n"); return ERR_CAST(clk); } @@ -618,7 +618,7 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) * No "adg" is not error */ clk = devm_clk_get(dev, "adg"); - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR(clk)) clk = rsnd_adg_null_clk_get(priv); adg->adg = clk; @@ -626,9 +626,9 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) for (i = 0; i < clkin_size; i++) { clk = devm_clk_get(dev, clkin_name[i]); - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR(clk)) clk = rsnd_adg_null_clk_get(priv); - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR(clk)) goto err; adg->clkin[i] = clk; diff --git a/sound/soc/renesas/siu_dai.c b/sound/soc/renesas/siu_dai.c index 039b1264d90d..85dee9025710 100644 --- a/sound/soc/renesas/siu_dai.c +++ b/sound/soc/renesas/siu_dai.c @@ -715,7 +715,6 @@ static struct snd_soc_dai_driver siu_i2s_dai = { static int siu_probe(struct platform_device *pdev) { - const struct firmware *fw_entry; struct resource *res, *region; struct siu_info *info; int ret; @@ -726,6 +725,7 @@ static int siu_probe(struct platform_device *pdev) siu_i2s_data = info; info->dev = &pdev->dev; + const struct firmware *fw_entry __free(firmware) = NULL; ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); if (ret) return ret; @@ -736,8 +736,6 @@ static int siu_probe(struct platform_device *pdev) */ memcpy(&info->fw, fw_entry->data, fw_entry->size); - release_firmware(fw_entry); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c index cf642a23c38a..541163ed56fc 100644 --- a/sound/soc/rockchip/rk3288_hdmi_analog.c +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -185,10 +185,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) gpiod_set_consumer_name(machine->gpio_hp_en, "hp_en"); ret = snd_soc_of_parse_card_name(card, "rockchip,model"); - if (ret) { - dev_err(card->dev, "SoC parse card name failed %d\n", ret); + if (ret) return ret; - } rk_dailink.codecs[0].of_node = of_parse_phandle(np, "rockchip,audio-codec", @@ -207,10 +205,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev) } ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name); - if (ret) { - dev_err(&pdev->dev, "Unable to get codec_dai_name\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Unable to get codec_dai_name\n"); rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller", 0); @@ -223,11 +220,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing"); - if (ret) { - dev_err(&pdev->dev, - "Unable to parse 'rockchip,routing' property\n"); + if (ret) return ret; - } snd_soc_card_set_drvdata(card, machine); diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 64c90316fa02..261f36d4c2fd 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -767,16 +767,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) /* try to prepare related clocks */ i2s->hclk = devm_clk_get_enabled(&pdev->dev, "i2s_hclk"); - if (IS_ERR(i2s->hclk)) { - dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n"); - return PTR_ERR(i2s->hclk); - } + if (IS_ERR(i2s->hclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->hclk), + "Can't retrieve i2s bus clock\n"); i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk"); - if (IS_ERR(i2s->mclk)) { - dev_err(&pdev->dev, "Can't retrieve i2s master clock\n"); - return PTR_ERR(i2s->mclk); - } + if (IS_ERR(i2s->mclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mclk), + "Can't retrieve i2s master clock\n"); regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) @@ -792,7 +790,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) i2s->bclk_ratio = 64; i2s->pinctrl = devm_pinctrl_get(&pdev->dev); - if (!IS_ERR(i2s->pinctrl)) { + if (IS_ERR(i2s->pinctrl)) { + if (PTR_ERR(i2s->pinctrl) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); + } else { i2s->bclk_on = pinctrl_lookup_state(i2s->pinctrl, "bclk_on"); if (!IS_ERR_OR_NULL(i2s->bclk_on)) { i2s->bclk_off = pinctrl_lookup_state(i2s->pinctrl, "bclk_off"); @@ -801,8 +803,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev) return -EINVAL; } } - } else { - dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); } i2s_pinctrl_select_bclk_off(i2s); @@ -831,16 +831,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev) &rockchip_i2s_component, dai, 1); - if (ret) { - dev_err(&pdev->dev, "Could not register DAI\n"); + if (ret) return ret; - } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "Could not register PCM\n"); + if (ret) return ret; - } return 0; } diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index e6229a325ffe..5ef7f109706d 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -1263,16 +1263,14 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "tx-m"); if (IS_ERR(i2s_tdm->tx_reset)) { - ret = PTR_ERR(i2s_tdm->tx_reset); - return dev_err_probe(i2s_tdm->dev, ret, + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->tx_reset), "Error in tx-m reset control\n"); } i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "rx-m"); if (IS_ERR(i2s_tdm->rx_reset)) { - ret = PTR_ERR(i2s_tdm->rx_reset); - return dev_err_probe(i2s_tdm->dev, ret, + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->rx_reset), "Error in rx-m reset control\n"); } @@ -1363,17 +1361,12 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &rockchip_i2s_tdm_component, i2s_tdm->dai, 1); - - if (ret) { - dev_err(&pdev->dev, "Could not register DAI\n"); + if (ret) goto err_suspend; - } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "Could not register PCM\n"); + if (ret) goto err_suspend; - } return 0; diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 075d0990a126..426506a8e18e 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -428,11 +428,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) /* Parse card name. */ ret = snd_soc_of_parse_card_name(card, "rockchip,model"); - if (ret) { - dev_err(&pdev->dev, - "Soc parse card name failed %d\n", ret); + if (ret) return ret; - } /* register the soc card */ ret = devm_snd_soc_register_card(&pdev->dev, card); diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index 115e90d3bbfe..343fda478901 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -630,10 +630,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) &rockchip_pdm_component, &rockchip_pdm_dai, 1); - if (ret) { - dev_err(&pdev->dev, "could not register dai: %d\n", ret); + if (ret) goto err_suspend; - } rockchip_pdm_rxctrl(pdm, 0); @@ -642,10 +640,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) goto err_suspend; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "could not register pcm: %d\n", ret); + if (ret) goto err_suspend; - } return 0; diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index 590b64b362f6..0432eeabc64e 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -191,11 +191,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; ret = snd_soc_of_parse_card_name(card, "rockchip,model"); - if (ret) { - dev_err(&pdev->dev, - "Soc parse card name failed %d\n", ret); + if (ret) goto put_cpu_of_node; - } ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 585e89f61f0d..30b5e71d0937 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -1428,12 +1428,13 @@ static int rockchip_sai_probe(struct platform_device *pdev) "Failed to initialize regmap\n"); irq = platform_get_irq_optional(pdev, 0); + if (irq == -EPROBE_DEFER) + return irq; if (irq > 0) { ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, IRQF_SHARED, node->name, sai); if (ret) - return dev_err_probe(&pdev->dev, ret, - "Failed to request irq %d\n", irq); + return ret; } else { dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq); } @@ -1456,7 +1457,7 @@ static int rockchip_sai_probe(struct platform_device *pdev) ret = rockchip_sai_parse_paths(sai, node); if (ret) - return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n"); + return ret; /* * From here on, all register accesses need to be wrapped in @@ -1471,18 +1472,14 @@ static int rockchip_sai_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n"); ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret); + if (ret) goto err_runtime_suspend; - } ret = devm_snd_soc_register_component(&pdev->dev, &rockchip_sai_component, dai, 1); - if (ret) { - dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + if (ret) goto err_runtime_suspend; - } pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_put(&pdev->dev); diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 7f15bc7f8f35..2d53efd5dd2e 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -396,13 +396,13 @@ static int rk_spdif_probe(struct platform_device *pdev) ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) - return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n"); + return ret; ret = devm_snd_soc_register_component(&pdev->dev, &rk_spdif_component, &rk_spdif_dai, 1); if (ret) - return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n"); + return ret; return 0; } diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 48ccc1d1854b..6db91b73f25c 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -658,6 +658,7 @@ static int aries_audio_probe(struct platform_device *pdev) goto out; } + of_node_get(aries_dai[0].cpus->of_node); aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node; /* Set CPU of_node for BT DAI */ diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f80f697a5d55..3e76cb81462f 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -8,6 +8,7 @@ #include <dt-bindings/sound/samsung-i2s.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/io.h> @@ -512,7 +513,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, u32 mod, mask, val = 0; int ret = 0; - pm_runtime_get_sync(dai->dev); + guard(pm_runtime_active)(dai->dev); scoped_guard(spinlock_irqsave, &priv->lock) mod = readl(priv->addr + I2SMOD); @@ -537,8 +538,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, && (mod & cdcon_mask))))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - ret = -EAGAIN; - goto err; + return -EAGAIN; } if (dir == SND_SOC_CLOCK_IN) @@ -566,7 +566,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, } else { priv->rclk_srcrate = clk_get_rate(priv->op_clk); - goto done; + return 0; } } @@ -580,14 +580,14 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, if (WARN_ON(IS_ERR(priv->op_clk))) { ret = PTR_ERR(priv->op_clk); priv->op_clk = NULL; - goto err; + return ret; } ret = clk_prepare_enable(priv->op_clk); if (ret) { clk_put(priv->op_clk); priv->op_clk = NULL; - goto err; + return ret; } priv->rclk_srcrate = clk_get_rate(priv->op_clk); @@ -595,11 +595,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, || (clk_id && !(mod & rsrc_mask))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - ret = -EAGAIN; - goto err; + return -EAGAIN; } else { /* Call can't be on the active DAI */ - goto done; + return 0; } if (clk_id == 1) @@ -607,8 +606,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, break; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); - ret = -EINVAL; - goto err; + return -EINVAL; } scoped_guard(spinlock_irqsave, &priv->lock) { @@ -616,13 +614,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, mod = (mod & ~mask) | val; writel(mod, priv->addr + I2SMOD); } -done: - pm_runtime_put(dai->dev); return 0; -err: - pm_runtime_put(dai->dev); - return ret; } static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) @@ -1123,15 +1116,31 @@ static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { { "Playback Mixer", NULL, "Primary Playback" }, - { "Playback Mixer", NULL, "Secondary Playback" }, - { "Mixer DAI TX", NULL, "Playback Mixer" }, { "Primary Capture", NULL, "Mixer DAI RX" }, }; +static const struct snd_soc_dapm_route samsung_i2s_dapm_routes_sec_play[] = { + { "Playback Mixer", NULL, "Secondary Playback" }, +}; + +static int samsung_i2s_component_probe(struct snd_soc_component *component) +{ + struct samsung_i2s_priv *priv = snd_soc_component_get_drvdata(component); + + if (priv->quirks & QUIRK_SEC_DAI) + snd_soc_dapm_add_routes(snd_soc_component_to_dapm(component), + samsung_i2s_dapm_routes_sec_play, + ARRAY_SIZE(samsung_i2s_dapm_routes_sec_play)); + + return 0; +} + static const struct snd_soc_component_driver samsung_i2s_component = { .name = "samsung-i2s", + .probe = samsung_i2s_component_probe, + .dapm_widgets = samsung_i2s_widgets, .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), @@ -1448,10 +1457,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) regs_base = res->start; priv->clk = devm_clk_get(&pdev->dev, "iis"); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "Failed to get iis clock\n"); - return PTR_ERR(priv->clk); - } + if (IS_ERR(priv->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), + "Failed to get iis clock\n"); + ret = clk_prepare_enable(priv->clk); if (ret != 0) { @@ -1657,8 +1666,7 @@ static const struct samsung_i2s_dai_data i2sv6_dai_type __maybe_unused = { }; static const struct samsung_i2s_dai_data i2sv7_dai_type __maybe_unused = { - .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | - QUIRK_SUPPORTS_TDM, + .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, .pcm_rates = SNDRV_PCM_RATE_8000_192000, .i2s_variant_regs = &i2sv7_regs, }; @@ -1682,7 +1690,7 @@ static const struct platform_device_id samsung_i2s_driver_ids[] = { .name = "samsung-i2s", .driver_data = (kernel_ulong_t)&i2sv3_dai_type, }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c index 2474eb619882..515e4dfc1432 100644 --- a/sound/soc/samsung/smdk_spdif.c +++ b/sound/soc/samsung/smdk_spdif.c @@ -130,12 +130,8 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, return ret; /* Set S/PDIF uses internal source clock */ - ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, - rclk_rate, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - return ret; + return snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, + rclk_rate, SND_SOC_CLOCK_IN); } static const struct snd_soc_ops smdk_spdif_ops = { diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 7fc46d55c522..53eaabaf8956 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -380,8 +380,8 @@ static int spdif_probe(struct platform_device *pdev) spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); if (IS_ERR(spdif->pclk)) { - dev_err(&pdev->dev, "failed to get peri-clock\n"); - ret = -ENOENT; + ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->pclk), + "failed to get peri-clock\n"); goto err0; } ret = clk_prepare_enable(spdif->pclk); @@ -390,8 +390,8 @@ static int spdif_probe(struct platform_device *pdev) spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); if (IS_ERR(spdif->sclk)) { - dev_err(&pdev->dev, "failed to get internal source clock\n"); - ret = -ENOENT; + ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->sclk), + "failed to get internal source clock\n"); goto err1; } ret = clk_prepare_enable(spdif->sclk); diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index b4dedba719dc..ce2e7c270765 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -364,7 +364,6 @@ static int entity_parse_ot(struct device *dev, /** * sdca_asoc_pde_poll_actual_ps - Verify PDE power state reached target state - * @dev: Pointer to the device for error logging. * @regmap: Register map for reading ACTUAL_PS register. * @function_id: SDCA function identifier. * @entity_id: SDCA entity identifier for the power domain. @@ -389,11 +388,11 @@ static int entity_parse_ot(struct device *dev, * polling times out before reaching the target state, or a negative error code if * a register read fails. */ -int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, +int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, int function_id, int entity_id, - int from_ps, int to_ps, - const struct sdca_pde_delay *pde_delays, - int num_delays) + int from_ps, int to_ps, + const struct sdca_pde_delay *pde_delays, + int num_delays) { static const int polls = 100; static const int default_poll_us = 1000; @@ -451,14 +450,14 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, return 0; } - ret = sdca_asoc_pde_poll_actual_ps(component->dev, component->regmap, + ret = sdca_asoc_pde_poll_actual_ps(component->regmap, SDW_SDCA_CTL_FUNC(widget->reg), SDW_SDCA_CTL_ENT(widget->reg), from, to, entity->pde.max_delay, entity->pde.num_max_delay); if (ret) - dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", + dev_err(component->dev, "%s: pde transition %x -> %x failed: %d\n", entity->label, from, to, ret); return ret; @@ -1231,7 +1230,7 @@ static u64 width_find_mask(unsigned int bits) } } -static int populate_rate_format(struct device *dev, +int sdca_asoc_populate_rate_format(struct device *dev, struct sdca_function_data *function, struct sdca_entity *entity, struct snd_soc_pcm_stream *stream) @@ -1292,6 +1291,7 @@ static int populate_rate_format(struct device *dev, return 0; } +EXPORT_SYMBOL_NS(sdca_asoc_populate_rate_format, "SND_SOC_SDCA"); /** * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function @@ -1344,7 +1344,7 @@ int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *funct stream->channels_min = 1; stream->channels_max = SDCA_MAX_CHANNEL_COUNT; - ret = populate_rate_format(dev, function, entity, stream); + ret = sdca_asoc_populate_rate_format(dev, function, entity, stream); if (ret) return ret; diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 2fb2b043c979..cc7045dc26e6 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -329,7 +329,7 @@ static int class_function_probe(struct auxiliary_device *auxdev, drv->core = core; drv->function = &sdev->function; - ret = sdca_parse_function(dev, core->sdw, drv->function); + ret = sdca_parse_function(dev, drv->function); if (ret) return ret; diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index dbe572336f8c..150e36ed24bc 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -195,7 +195,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, { struct device *dev = interrupt->dev; struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; - const struct firmware *firmware = NULL; struct acpi_sw_file *swf = NULL, *tmp; struct sdca_fdl_file *fdl_file; char *disk_filename; @@ -230,6 +229,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); + const struct firmware *firmware __free(firmware) = NULL; ret = firmware_request_nowarn(&firmware, disk_filename, dev); kfree(disk_filename); if (ret) { @@ -258,8 +258,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, if (!swf) { dev_err(dev, "failed to locate SWF\n"); - ret = -ENOENT; - goto error; + return -ENOENT; } dev_info(dev, "loading SWF: %x-%x-%x\n", @@ -271,9 +270,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, swf->file_length - offsetof(struct acpi_sw_file, data)); - -error: - release_firmware(firmware); return ret; } diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c index b5ca98283a88..54604872ae0d 100644 --- a/sound/soc/sdca/sdca_function_device.c +++ b/sound/soc/sdca/sdca_function_device.c @@ -32,7 +32,8 @@ static void sdca_dev_release(struct device *dev) /* alloc, init and add link devices */ static struct sdca_dev *sdca_dev_register(struct device *parent, - struct sdca_function_desc *function_desc) + struct sdca_function_desc *function_desc, + struct acpi_table_swft *swft) { struct sdca_dev *sdev; struct auxiliary_device *auxdev; @@ -50,6 +51,7 @@ static struct sdca_dev *sdca_dev_register(struct device *parent, auxdev->dev.release = sdca_dev_release; sdev->function.desc = function_desc; + sdev->function.fdl_data.swft = swft; rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); if (rc < 0) { @@ -99,7 +101,8 @@ int sdca_dev_register_functions(struct sdw_slave *slave) struct sdca_dev *func_dev; func_dev = sdca_dev_register(&slave->dev, - &sdca_data->function[i]); + &sdca_data->function[i], + sdca_data->swft); if (IS_ERR(func_dev)) { ret = PTR_ERR(func_dev); /* diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 7a7a9f1a4938..e01d91eb3cc8 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -7,6 +7,7 @@ */ #include <linux/acpi.h> +#include <linux/array_size.h> #include <linux/byteorder/generic.h> #include <linux/cleanup.h> #include <linux/device.h> @@ -18,7 +19,6 @@ #include <linux/types.h> #include <sound/sdca.h> #include <sound/sdca_function.h> -#include <sound/sdca_hid.h> /* * Should be long enough to encompass all the MIPI DisCo properties. @@ -1365,90 +1365,76 @@ bad_list: return -EINVAL; } -static int -find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, - struct fwnode_handle *function_node, - struct fwnode_handle *entity_node, struct sdca_entity *entity) +static int find_sdca_entity_hide(struct device *dev, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) { struct sdca_entity_hide *hide = &entity->hide; - unsigned int delay, *af_list = hide->af_number_list; - int nval, ret; - unsigned char *report_desc = NULL; + int num_reports, ret; + unsigned int delay; ret = fwnode_property_read_u32(entity_node, - "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay); + "mipi-sdca-RxUMP-ownership-transition-max-delay", + &delay); if (!ret) hide->max_delay = delay; - nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids"); - if (nval > 0) { - hide->num_hidtx_ids = nval; + num_reports = fwnode_property_count_u32(entity_node, + "mipi-sdca-HIDTx-supported-report-ids"); + if (num_reports < 0 && num_reports != -EINVAL) { + dev_err(dev, "%pfwP: failed to read hid tx ids: %d\n", + entity_node, num_reports); + return num_reports; + } else if (num_reports > 0) { + hide->num_hidtx_ids = num_reports; hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids, sizeof(*hide->hidtx_ids), GFP_KERNEL); if (!hide->hidtx_ids) return -ENOMEM; - ret = fwnode_property_read_u32_array(entity_node, - "mipi-sdca-HIDTx-supported-report-ids", - hide->hidtx_ids, - hide->num_hidtx_ids); - if (ret < 0) - return ret; + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDTx-supported-report-ids", + hide->hidtx_ids, hide->num_hidtx_ids); } - nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids"); - if (nval > 0) { - hide->num_hidrx_ids = nval; + num_reports = fwnode_property_count_u32(entity_node, + "mipi-sdca-HIDRx-supported-report-ids"); + if (num_reports < 0 && num_reports != -EINVAL) { + dev_err(dev, "%pfwP: failed to read hid rx ids: %d\n", + entity_node, num_reports); + return num_reports; + } else if (num_reports > 0) { + hide->num_hidrx_ids = num_reports; hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids, sizeof(*hide->hidrx_ids), GFP_KERNEL); if (!hide->hidrx_ids) return -ENOMEM; - ret = fwnode_property_read_u32_array(entity_node, - "mipi-sdca-HIDRx-supported-report-ids", - hide->hidrx_ids, - hide->num_hidrx_ids); - if (ret < 0) - return ret; + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDRx-supported-report-ids", + hide->hidrx_ids, hide->num_hidrx_ids); } - nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list"); - if (nval <= 0) { + /* + * FIXME: This should probably link to the actual sdca_function_data pointer, + * but updating to do so should probably wait until we have a user. + */ + num_reports = fwnode_property_count_u32(entity_node, + "mipi-sdca-hide-related-audio-function-list"); + if (num_reports <= 0) { dev_err(dev, "%pfwP: audio function numbers list missing: %d\n", - entity_node, nval); + entity_node, num_reports); return -EINVAL; - } else if (nval > SDCA_MAX_FUNCTION_COUNT) { - dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node); + } else if (num_reports > ARRAY_SIZE(hide->af_number_list)) { + dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", + entity_node); return -EINVAL; } - hide->hide_reside_function_num = nval; + hide->hide_reside_function_num = num_reports; fwnode_property_read_u32_array(entity_node, - "mipi-sdca-hide-related-audio-function-list", af_list, nval); - - nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); - if (nval) - fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", - (u8 *)&hide->hid_desc, nval); - - if (hide->hid_desc.bNumDescriptors) { - nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); - if (nval) { - report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); - if (!report_desc) - return -ENOMEM; - hide->hid_report_desc = report_desc; - fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", - report_desc, nval); - - /* add HID device */ - ret = sdca_add_hid_device(dev, sdw, entity); - if (ret) { - dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); - return ret; - } - } - } + "mipi-sdca-hide-related-audio-function-list", + hide->af_number_list, num_reports); return 0; } @@ -1475,8 +1461,7 @@ static int find_sdca_entity_xu(struct device *dev, return 0; } -static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_data *function, +static int find_sdca_entity(struct device *dev, struct sdca_function_data *function, struct fwnode_handle *function_node, struct fwnode_handle *entity_node, struct sdca_entity *entity) @@ -1528,8 +1513,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, ret = find_sdca_entity_ge(dev, entity_node, entity); break; case SDCA_ENTITY_TYPE_HIDE: - ret = find_sdca_entity_hide(dev, sdw, function_node, - entity_node, entity); + ret = find_sdca_entity_hide(dev, entity_node, entity); break; default: break; @@ -1544,8 +1528,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, return 0; } -static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, - struct fwnode_handle *function_node, +static int find_sdca_entities(struct device *dev, struct fwnode_handle *function_node, struct sdca_function_data *function) { struct sdca_entity *entities; @@ -1596,7 +1579,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, return -EINVAL; } - ret = find_sdca_entity(dev, sdw, function, function_node, + ret = find_sdca_entity(dev, function, function_node, entity_node, &entities[i]); fwnode_handle_put(entity_node); if (ret) @@ -1619,7 +1602,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, return 0; } -static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function, +struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, const char *entity_label) { struct sdca_entity *entity = NULL; @@ -1648,6 +1631,7 @@ static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data * return NULL; } +EXPORT_SYMBOL_NS(sdca_find_entity_by_label, "SND_SOC_SDCA"); static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function, const int id) @@ -1688,7 +1672,7 @@ static int find_sdca_entity_connection_iot(struct device *dev, return ret; } - clock_entity = find_sdca_entity_by_label(function, clock_label); + clock_entity = sdca_find_entity_by_label(function, clock_label); if (!clock_entity) { dev_err(dev, "%s: failed to find clock with label %s\n", entity->label, clock_label); @@ -1873,7 +1857,7 @@ static int find_sdca_entity_connection(struct device *dev, return ret; } - connected_entity = find_sdca_entity_by_label(function, connected_label); + connected_entity = sdca_find_entity_by_label(function, connected_label); if (!connected_entity) { dev_err(dev, "%s: failed to find entity with label %s\n", entity->label, connected_label); @@ -2088,8 +2072,7 @@ static int find_sdca_clusters(struct device *dev, return 0; } -static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, - struct fwnode_handle *function_node, +static int find_sdca_filesets(struct device *dev, struct fwnode_handle *function_node, struct sdca_function_data *function) { static const int mult_fileset = 3; @@ -2171,23 +2154,65 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, set->files = files; } - function->fdl_data.swft = sdw->sdca_data.swft; function->fdl_data.num_sets = num_sets; function->fdl_data.sets = sets; return 0; } +static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node, + struct sdca_function_data *function) +{ + int num_desc; + + num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); + if (!num_desc) { + return 0; + } else if (num_desc < 0) { + dev_err(dev, "%pfwP: failed to read hid descriptor: %d\n", + function_node, num_desc); + return num_desc; + } else if (num_desc > sizeof(function->hid.desc)) { + dev_err(dev, "%pfwP: hid descriptor too large: %d\n", + function_node, num_desc); + return -EINVAL; + } + + fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", + (u8 *)&function->hid.desc, num_desc); + + if (!function->hid.desc.bNumDescriptors) + return 0; + + num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); + if (num_desc <= 0) { + dev_err(dev, "%pfwP: failed to read report descriptor: %d\n", + function_node, num_desc); + + if (!num_desc) + return -EINVAL; + + return num_desc; + } + + function->hid.report_desc = devm_kzalloc(dev, num_desc, GFP_KERNEL); + if (!function->hid.report_desc) + return -ENOMEM; + + fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", + function->hid.report_desc, num_desc); + + return 0; +} + /** * sdca_parse_function - parse ACPI DisCo for a Function * @dev: Pointer to device against which function data will be allocated. - * @sdw: SoundWire slave device to be processed. * @function: Pointer to the Function information, to be populated. * * Return: Returns 0 for success. */ -int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_data *function) +int sdca_parse_function(struct device *dev, struct sdca_function_data *function) { struct fwnode_handle *node = function->desc->node; u32 tmp; @@ -2213,7 +2238,7 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, if (ret) return ret; - ret = find_sdca_entities(dev, sdw, node, function); + ret = find_sdca_entities(dev, node, function); if (ret) return ret; @@ -2225,10 +2250,20 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, if (ret < 0) return ret; - ret = find_sdca_filesets(dev, sdw, node, function); + ret = find_sdca_filesets(dev, node, function); if (ret) return ret; + switch (function->desc->type) { + case SDCA_FUNCTION_TYPE_HID: + ret = find_sdca_hid(dev, node, function); + if (ret) + return ret; + break; + default: + break; + } + return 0; } EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index abbd56a3d297..bee1b83c05f2 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/property.h> #include <linux/soundwire/sdw.h> +#include <linux/string.h> #include <linux/types.h> #include <sound/sdca.h> #include <sound/sdca_function.h> @@ -23,18 +24,18 @@ static int sdwhid_parse(struct hid_device *hid) { - struct sdca_entity *entity = hid->driver_data; + struct sdca_function_data *function = hid->driver_data; unsigned int rsize; int ret; - rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength); + rsize = le16_to_cpu(function->hid.desc.rpt_desc.wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); return -EINVAL; } - ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize); + ret = hid_parse_report(hid, function->hid.report_desc, rsize); if (!ret) return 0; @@ -85,10 +86,16 @@ static const struct hid_ll_driver sdw_hid_driver = { .raw_request = sdwhid_raw_request, }; -int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - struct sdca_entity *entity) +/** + * sdca_add_hid_device - create a new SDCA HID device + * @interrupt: Pointer to the SDCA interrupt information structure. + * + * Return: Zero on success, and a negative error code on failure. + */ +int sdca_add_hid_device(struct sdca_interrupt *interrupt) { - struct sdw_bus *bus = sdw->bus; + struct device *dev = interrupt->dev; + struct sdca_function_data *function = interrupt->function; struct hid_device *hid; int ret; @@ -100,16 +107,13 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, hid->dev.parent = dev; hid->bus = BUS_SDW; - hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); - - snprintf(hid->name, sizeof(hid->name), - "HID sdw:%01x:%01x:%04x:%04x:%02x", - bus->controller_id, bus->link_id, sdw->id.mfg_id, - sdw->id.part_id, sdw->id.class_id); + hid->version = le16_to_cpu(function->hid.desc.bcdHID); - snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); + strscpy(hid->phys, dev_name(dev)); + snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x", + function->desc->name, function->desc->adr); - hid->driver_data = entity; + hid->driver_data = function; ret = hid_add_device(hid); if (ret && ret != -ENODEV) { @@ -118,13 +122,25 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, return ret; } - entity->hide.hid = hid; + interrupt->priv = hid; return 0; } EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); /** + * sdca_destroy_hid_device - destroy the HID device + * @interrupt: Pointer to the SDCA interrupt information structure. + */ +void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) +{ + struct hid_device *hid = interrupt->priv; + + hid_destroy_device(hid); +} +EXPORT_SYMBOL_NS(sdca_destroy_hid_device, "SND_SOC_SDCA"); + +/** * sdca_hid_process_report - read a HID event from the device and report * @interrupt: Pointer to the SDCA interrupt information structure. * @@ -133,7 +149,7 @@ EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); int sdca_hid_process_report(struct sdca_interrupt *interrupt) { struct device *dev = interrupt->dev; - struct hid_device *hid = interrupt->entity->hide.hid; + struct hid_device *hid = interrupt->priv; void *val __free(kfree) = NULL; int len, ret; diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 42fbd3af8a75..7aebc721a847 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -487,6 +487,12 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, } break; case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + interrupt->free_priv = sdca_destroy_hid_device; + + ret = sdca_add_hid_device(interrupt); + if (ret) + return ret; + interrupt->handler = hid_handler; break; default: diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c index e0e32a279787..129a437ae397 100644 --- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c @@ -99,11 +99,9 @@ static const struct snd_soc_dai_link bridge_dai_template = { SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai), }; -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs) { - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); - if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) { (*num_dais)++; (*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index d8db8fc5313e..8a07ba2a29e5 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -836,6 +836,35 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, { .vendor_id = 0x01fa, + .part_id = 0x3562, + .name_prefix = "AMP", + .is_amp = true, + .dais = { + { + .direction = {true, false}, + .dai_name = "cs35l56-sdw1", + .component_name = "cs35l56", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .init = asoc_sdw_cs_amp_init, + .rtd_init = asoc_sdw_cs_spk_rtd_init, + .controls = generic_spk_controls, + .num_controls = ARRAY_SIZE(generic_spk_controls), + .widgets = generic_spk_widgets, + .num_widgets = ARRAY_SIZE(generic_spk_widgets), + }, + { + .direction = {false, true}, + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, + .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, + }, + { + .vendor_id = 0x01fa, .part_id = 0x3563, .name_prefix = "AMP", .is_amp = true, @@ -1789,9 +1818,7 @@ static int is_sdca_aux_dev_present(struct device *dev, int adr_index) { struct sdw_slave *slave; - struct device *sdw_dev; const char *sdw_codec_name; - int ret = 0; int i; if (!aux_codec_name) @@ -1801,7 +1828,8 @@ static int is_sdca_aux_dev_present(struct device *dev, if (!sdw_codec_name) return -ENOMEM; - sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + struct device *sdw_dev __free(put_device) = + bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); if (!sdw_dev) { dev_err(dev, "codec %s not found\n", sdw_codec_name); return -EINVAL; @@ -1811,24 +1839,19 @@ static int is_sdca_aux_dev_present(struct device *dev, if (!slave->sdca_data.interface_revision) { dev_warn(dev, "No SDCA properties, assuming aux '%s' present\n", aux_codec_name); - ret = 1; - goto put_dev; + return 1; } for (i = 0; i < slave->sdca_data.num_functions; i++) { const char *fname = slave->sdca_data.function[i].name; - if (fname && strstr(aux_codec_name, fname)) { - ret = 1; - goto put_dev; - } + if (fname && strstr(aux_codec_name, fname)) + return 1; } dev_dbg(dev, "SDCA function for aux '%s' NOT FOUND on slave, skipping\n", aux_codec_name); -put_dev: - put_device(sdw_dev); - return ret; + return 0; } int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, @@ -1928,9 +1951,8 @@ static int is_sdca_endpoint_present(struct device *dev, const struct snd_soc_acpi_endpoint *adr_end; const struct asoc_sdw_dai_info *dai_info; struct sdw_slave *slave; - struct device *sdw_dev; const char *sdw_codec_name; - int ret, i; + int i; adr_end = &adr_dev->endpoints[end_index]; dai_info = &codec_info->dais[adr_end->num]; @@ -1939,7 +1961,8 @@ static int is_sdca_endpoint_present(struct device *dev, if (!sdw_codec_name) return -ENOMEM; - sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + struct device *sdw_dev __free(put_device) = + bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); if (!sdw_dev) { dev_err(dev, "codec %s not found\n", sdw_codec_name); return -EINVAL; @@ -1950,8 +1973,7 @@ static int is_sdca_endpoint_present(struct device *dev, /* Make sure BIOS provides SDCA properties */ if (!slave->sdca_data.interface_revision) { dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); - ret = 1; - goto put_device; + return 1; } for (i = 0; i < slave->sdca_data.num_functions; i++) { @@ -1960,8 +1982,7 @@ static int is_sdca_endpoint_present(struct device *dev, if (dai_type == dai_info->dai_type) { dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", dai_type, slave->sdca_data.function[i].name); - ret = 1; - goto put_device; + return 1; } } @@ -1969,21 +1990,16 @@ static int is_sdca_endpoint_present(struct device *dev, "SDCA device function for DAI type %d not supported, skip endpoint\n", dai_info->dai_type); - ret = 0; - -put_device: - put_device(sdw_dev); - return ret; + return 0; } -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, int *num_devs) { - struct device *dev = card->dev; - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; const struct snd_soc_acpi_link_adr *adr_link; @@ -2045,7 +2061,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; if (codec_info->count_sidecar && codec_info->add_sidecar) { - ret = codec_info->count_sidecar(card, &num_dais, num_devs); + ret = codec_info->count_sidecar(ctx, &num_dais, num_devs); if (ret) return ret; diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 2ce24513fac5..dc7d203cb76a 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -29,17 +29,42 @@ static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component, func, component->name, reg); } -static inline int soc_component_field_shift(struct snd_soc_component *component, - unsigned int mask) +struct snd_soc_component *snd_soc_component_alloc(struct device *dev) { - if (!mask) { - dev_err(component->dev, "ASoC: error field mask is zero for %s\n", - component->name); - return 0; - } + struct snd_soc_component *component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); - return (ffs(mask) - 1); + if (!component) + return NULL; + + component->dev = dev; + + return component; +} +EXPORT_SYMBOL_GPL(snd_soc_component_alloc); + +void snd_soc_component_set_name(struct snd_soc_component *component, const char *name) +{ + component->name = name; +} +EXPORT_SYMBOL_GPL(snd_soc_component_set_name); + +const char *snd_soc_component_name(struct snd_soc_component *component) +{ + return component->name; +} +EXPORT_SYMBOL_GPL(snd_soc_component_name); + +void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv) +{ + component->priv = priv; +} +EXPORT_SYMBOL_GPL(snd_soc_component_set_priv); + +void *snd_soc_component_to_priv(struct snd_soc_component *component) +{ + return component->priv; } +EXPORT_SYMBOL_GPL(snd_soc_component_to_priv); /* * We might want to check substream by using list. @@ -820,6 +845,18 @@ int snd_soc_component_update_bits_async(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); +static inline int soc_component_field_shift(struct snd_soc_component *component, + unsigned int mask) +{ + if (!mask) { + dev_err(component->dev, "ASoC: error field mask is zero for %s\n", + component->name); + return 0; + } + + return (ffs(mask) - 1); +} + /** * snd_soc_component_read_field() - Read register field value * @component: Component to read from diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 44f9bb4473f5..b9c70268d49f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2707,10 +2707,11 @@ static void snd_soc_del_component_unlocked(struct snd_soc_component *component) list_del(&component->list); } -int snd_soc_component_initialize(struct snd_soc_component *component, - const struct snd_soc_component_driver *driver, - struct device *dev) +static int soc_component_initialize(struct snd_soc_component *component, + const struct snd_soc_component_driver *driver) { + struct device *dev = component->dev; + component->dapm = snd_soc_dapm_alloc(dev); if (!component->dapm) return -ENOMEM; @@ -2730,16 +2731,14 @@ int snd_soc_component_initialize(struct snd_soc_component *component, } } - component->dev = dev; component->driver = driver; return 0; } -EXPORT_SYMBOL_GPL(snd_soc_component_initialize); -int snd_soc_add_component(struct snd_soc_component *component, - struct snd_soc_dai_driver *dai_drv, - int num_dai) +static int soc_component_add(struct snd_soc_component *component, + struct snd_soc_dai_driver *dai_drv, + int num_dai) { struct snd_soc_card *card, *c; int ret; @@ -2778,27 +2777,36 @@ err_cleanup: return ret; } -EXPORT_SYMBOL_GPL(snd_soc_add_component); -int snd_soc_register_component(struct device *dev, +int snd_soc_register_component_c(struct snd_soc_component *component, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai) { - struct snd_soc_component *component; int ret; - component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); - if (!component) - return -ENOMEM; - - ret = snd_soc_component_initialize(component, component_driver, dev); + ret = soc_component_initialize(component, component_driver); if (ret < 0) return ret; - return snd_soc_add_component(component, dai_drv, num_dai); + return soc_component_add(component, dai_drv, num_dai); +} +EXPORT_SYMBOL_GPL(snd_soc_register_component_c); + +int snd_soc_register_component_d(struct device *dev, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + struct snd_soc_component *component; + + component = snd_soc_component_alloc(dev); + if (!component) + return -ENOMEM; + + return snd_soc_register_component_c(component, component_driver, dai_drv, num_dai); } -EXPORT_SYMBOL_GPL(snd_soc_register_component); +EXPORT_SYMBOL_GPL(snd_soc_register_component_d); /** * snd_soc_unregister_component_by_driver - Unregister component using a given driver diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4ad126bd4f70..c98e917cc911 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1203,6 +1203,8 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, case snd_soc_dapm_pga: case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: + case snd_soc_dapm_encoder: + case snd_soc_dapm_decoder: wname_in_long_name = true; kcname_in_long_name = true; break; @@ -2785,6 +2787,8 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *component, case snd_soc_dapm_pga: case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: + case snd_soc_dapm_encoder: + case snd_soc_dapm_decoder: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_supply: @@ -3367,6 +3371,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) case snd_soc_dapm_pga: case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: + case snd_soc_dapm_encoder: + case snd_soc_dapm_decoder: dapm_new_pga(w); break; case snd_soc_dapm_dai_link: diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 467426d2b5e4..3b18d90e81c3 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -3,6 +3,7 @@ // Copyright (C) 2013, Analog Devices Inc. // Author: Lars-Peter Clausen <lars@metafoo.de> +#include <linux/acpi.h> #include <linux/module.h> #include <linux/init.h> #include <linux/dmaengine.h> @@ -77,7 +78,7 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); struct dma_slave_config slave_config; int ret; @@ -99,7 +100,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); struct device *dma_dev = dmaengine_dma_dev(pcm, substream); struct dma_chan *chan = pcm->chan[substream->stream]; struct snd_dmaengine_dai_dma_data *dma_data; @@ -148,7 +149,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, static int dmaengine_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); struct dma_chan *chan = pcm->chan[substream->stream]; int ret; @@ -176,7 +177,7 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); struct snd_dmaengine_dai_dma_data *dma_data; if (rtd->dai_link->num_cpus > 1) { @@ -219,7 +220,7 @@ static bool dmaengine_pcm_can_report_residue(struct device *dev, static int dmaengine_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); const struct snd_dmaengine_pcm_config *config = pcm->config; struct device *dev = component->dev; size_t prealloc_buffer_size; @@ -279,7 +280,7 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) return snd_dmaengine_pcm_pointer_no_residue(substream); @@ -293,7 +294,7 @@ static int dmaengine_copy(struct snd_soc_component *component, struct iov_iter *iter, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); int (*process)(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, unsigned long bytes) = pcm->config->process; @@ -395,6 +396,27 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, */ if (PTR_ERR(chan) == -EPROBE_DEFER) return -EPROBE_DEFER; + + bool has_fw_node = dev->of_node || is_acpi_device_node(dev->fwnode); + bool name_exists_in_fw = false; + + if (has_fw_node) + name_exists_in_fw = device_property_match_string(dev, + "dma-names", + name) >= 0; + + if (has_fw_node && name_exists_in_fw) + dev_warn(dev, "DTS/ACPI DMA channel '%s' request failed (%ld)\n", + name, PTR_ERR(chan)); + + if (has_fw_node && !name_exists_in_fw) + dev_warn(dev, "DTS/ACPI name '%s' not found, legacy failed (%ld)\n", + name, PTR_ERR(chan)); + + if (!has_fw_node) + dev_warn(dev, "Legacy DMA channel '%s' request failed (%ld)\n", + name, PTR_ERR(chan)); + pcm->chan[i] = NULL; } else { pcm->chan[i] = chan; @@ -406,6 +428,12 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) pcm->chan[1] = pcm->chan[0]; + if (!pcm->chan[0] && + !pcm->chan[1]) { + dev_err(dev, "no DMA channel found for either playback or capture\n"); + return -ENODEV; + } + return 0; } @@ -435,10 +463,15 @@ static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config = int snd_dmaengine_pcm_register(struct device *dev, const struct snd_dmaengine_pcm_config *config, unsigned int flags) { + struct snd_soc_component *component; const struct snd_soc_component_driver *driver; struct dmaengine_pcm *pcm; int ret; + component = snd_soc_component_alloc(dev); + if (!component) + return -ENOMEM; + pcm = kzalloc_obj(*pcm); if (!pcm) return -ENOMEM; @@ -449,7 +482,8 @@ int snd_dmaengine_pcm_register(struct device *dev, pcm->flags = flags; if (config->name) - pcm->component.name = config->name; + snd_soc_component_set_name(component, config->name); + snd_soc_component_set_priv(component, pcm); ret = dmaengine_pcm_request_chan_of(pcm, dev, config); if (ret) @@ -460,11 +494,7 @@ int snd_dmaengine_pcm_register(struct device *dev, else driver = &dmaengine_pcm_component; - ret = snd_soc_component_initialize(&pcm->component, driver, dev); - if (ret) - goto err_free_dma; - - ret = snd_soc_add_component(&pcm->component, NULL, 0); + ret = snd_soc_register_component(component, driver, NULL, 0); if (ret) goto err_free_dma; @@ -493,7 +523,7 @@ void snd_dmaengine_pcm_unregister(struct device *dev) if (!component) return; - pcm = soc_component_to_pcm(component); + pcm = snd_soc_component_to_priv(component); snd_soc_unregister_component_by_driver(dev, component->driver); dmaengine_pcm_release_chan(pcm); diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c index c8f2ec29e970..346a52d3006a 100644 --- a/sound/soc/soc-topology-test.c +++ b/sound/soc/soc-topology-test.c @@ -45,15 +45,13 @@ static void snd_soc_tplg_test_exit(struct kunit *test) struct kunit_soc_component { struct kunit *kunit; int expect; /* what result we expect when loading topology */ - struct snd_soc_component comp; struct snd_soc_card card; struct firmware fw; }; static int d_probe(struct snd_soc_component *component) { - struct kunit_soc_component *kunit_comp = - container_of(component, struct kunit_soc_component, comp); + struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); int ret; ret = snd_soc_tplg_component_load(component, NULL, &kunit_comp->fw); @@ -65,8 +63,7 @@ static int d_probe(struct snd_soc_component *component) static void d_remove(struct snd_soc_component *component) { - struct kunit_soc_component *kunit_comp = - container_of(component, struct kunit_soc_component, comp); + struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); int ret; ret = snd_soc_tplg_component_remove(component); @@ -214,8 +211,7 @@ static struct tplg_tmpl_002 tplg_tmpl_with_pcm = { */ static int d_probe_null_comp(struct snd_soc_component *component) { - struct kunit_soc_component *kunit_comp = - container_of(component, struct kunit_soc_component, comp); + struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); int ret; /* instead of passing component pointer as first argument, pass NULL here */ @@ -234,6 +230,7 @@ static const struct snd_soc_component_driver test_component_null_comp = { static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; int ret; /* prepare */ @@ -249,15 +246,17 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_comp, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component_null_comp, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -276,6 +275,7 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; int ret; /* prepare */ @@ -291,15 +291,17 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -318,8 +320,7 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) */ static int d_probe_null_fw(struct snd_soc_component *component) { - struct kunit_soc_component *kunit_comp = - container_of(component, struct kunit_soc_component, comp); + struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); int ret; /* instead of passing fw pointer as third argument, pass NULL here */ @@ -338,6 +339,7 @@ static const struct snd_soc_component_driver test_component_null_fw = { static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; int ret; /* prepare */ @@ -353,15 +355,17 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_fw, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component_null_fw, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -375,6 +379,7 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -401,15 +406,17 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -425,6 +432,7 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -456,15 +464,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -480,6 +490,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -511,15 +522,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -535,6 +548,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -566,15 +580,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -590,6 +606,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -622,15 +639,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -644,6 +663,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; u8 *data; int size; int ret; @@ -670,15 +690,17 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); snd_soc_unregister_component(test_dev); @@ -693,6 +715,7 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; u8 *data; int size; int ret; @@ -720,16 +743,19 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); for (i = 0; i < 100; i++) { - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); snd_soc_unregister_component(test_dev); @@ -745,6 +771,7 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; u8 *data; int size; int ret; @@ -772,11 +799,13 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; - /* run test */ - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + snd_soc_component_set_priv(component, kunit_comp); + + /* run test */ + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); for (i = 0; i < 100; i++) { diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index 05faf1c6d6fc..903e7ec3b3ba 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -104,4 +104,15 @@ config SND_SOC_SOF_AMD_ACP70 AMD ACP7.0/ACP7.1 version based platforms. Say Y if you want to enable SOF on ACP7.0/ACP7.1 based platforms. +config SND_SOC_SOF_AMD_ACP7X + tristate "SOF support for ACP7.B/7.F platforms" + depends on SND_SOC_SOF_PCI + depends on AMD_NODE + select SND_SOC_SOF_AMD_COMMON + help + Select this option for SOF support on AMD ACP7.B and ACP7.F PCI + revision based platforms. + Say Y if you want to enable SOF on ACP7.B/7.F based platforms. + If unsure select "N". + endif diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile index 6ae39fd5a836..bb1c0ddef69d 100644 --- a/sound/soc/sof/amd/Makefile +++ b/sound/soc/sof/amd/Makefile @@ -11,6 +11,7 @@ snd-sof-amd-rembrandt-y := pci-rmb.o rembrandt.o snd-sof-amd-vangogh-y := pci-vangogh.o vangogh.o snd-sof-amd-acp63-y := pci-acp63.o acp63.o snd-sof-amd-acp70-y := pci-acp70.o acp70.o +snd-sof-amd-acp7x-y := pci-acp7x.o acp7x.o obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) += snd-sof-amd-renoir.o @@ -18,3 +19,4 @@ obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) += snd-sof-amd-rembrandt.o obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) += snd-sof-amd-vangogh.o obj-$(CONFIG_SND_SOC_SOF_AMD_ACP63) += snd-sof-amd-acp63.o obj-$(CONFIG_SND_SOC_SOF_AMD_ACP70) += snd-sof-amd-acp70.o +obj-$(CONFIG_SND_SOC_SOF_AMD_ACP7X) += snd-sof-amd-acp7x.o diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index 08583a91afbc..bea1bd3afa70 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -68,12 +68,14 @@ #define ACP5X_PGFSM_BASE 0x1424 #define ACP6X_PGFSM_BASE 0x1024 #define ACP70_PGFSM_BASE ACP6X_PGFSM_BASE +#define ACP7X_PGFSM_BASE ACP6X_PGFSM_BASE #define PGFSM_CONTROL_OFFSET 0x0 #define PGFSM_STATUS_OFFSET 0x4 #define ACP3X_CLKMUX_SEL 0x1424 #define ACP5X_CLKMUX_SEL 0x142C #define ACP6X_CLKMUX_SEL 0x102C #define ACP70_CLKMUX_SEL ACP6X_CLKMUX_SEL +#define ACP7X_CLKMUX_SEL ACP6X_CLKMUX_SEL /* Registers from ACP_INTR block */ #define ACP3X_EXT_INTR_STAT 0x1808 @@ -86,21 +88,31 @@ #define ACP70_EXTERNAL_INTR_CNTL ACP6X_EXTERNAL_INTR_CNTL #define ACP70_EXT_INTR_STAT ACP6X_EXT_INTR_STAT #define ACP70_EXT_INTR_STAT1 ACP6X_EXT_INTR_STAT1 +#define ACP7X_EXTERNAL_INTR_ENB ACP6X_EXTERNAL_INTR_ENB +#define ACP7X_EXTERNAL_INTR_CNTL 0x1A04 +#define ACP7X_EXT_INTR_STAT 0x1A1C +#define ACP7X_EXTERNAL_INTR_CNTL1 0x1A08 +#define ACP7X_EXT_INTR_STAT1 0x1A20 #define ACP3X_DSP_SW_INTR_BASE 0x1814 #define ACP5X_DSP_SW_INTR_BASE 0x1814 #define ACP6X_DSP_SW_INTR_BASE 0x1808 #define ACP70_DSP_SW_INTR_BASE ACP6X_DSP_SW_INTR_BASE +#define ACP7X_DSP_SW_INTR_BASE 0x1860 #define DSP_SW_INTR_CNTL_OFFSET 0x0 #define DSP_SW_INTR_STAT_OFFSET 0x4 +#define ACP7X_DSP_SW_INTR_STAT (ACP7X_DSP_SW_INTR_BASE + DSP_SW_INTR_STAT_OFFSET) #define DSP_SW_INTR_TRIG_OFFSET 0x8 +#define ACP7X_DSP_SW_INTR_TRIG_OFFSET 0x30 #define ACP3X_ERROR_STATUS 0x18C4 #define ACP6X_ERROR_STATUS 0x1A4C #define ACP70_ERROR_STATUS ACP6X_ERROR_STATUS +#define ACP7X_ERROR_STATUS 0x1A88 #define ACP3X_AXI2DAGB_SEM_0 0x1880 #define ACP5X_AXI2DAGB_SEM_0 0x1884 #define ACP6X_AXI2DAGB_SEM_0 0x1874 #define ACP70_AXI2DAGB_SEM_0 ACP6X_AXI2DAGB_SEM_0 +#define ACP7X_AXI2DAGB_SEM_0 0x18F4 /* ACP common registers to report errors related to I2S & SoundWire interfaces */ #define ACP3X_SW_I2S_ERROR_REASON 0x18C8 @@ -123,6 +135,7 @@ #define ACP_SCRATCH_REG_0 0x10000 #define ACP6X_DSP_FUSION_RUNSTALL 0x0644 #define ACP70_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL +#define ACP7X_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL /* Cache window registers */ #define ACP_DSP0_CACHE_OFFSET0 0x0420 @@ -139,5 +152,9 @@ #define ACP70_SDW1_HOST_WAKE_STAT BIT(25) #define ACP70_SDW0_PME_STAT BIT(26) #define ACP70_SDW1_PME_STAT BIT(27) +#define ACP7X_DSP0_IDMA_ERROR_MASK 0x4B0 +#define ACP7X_IDMA_ERROR_MASK 0x1FF9FF +#define ACP7X_ZSC_DSP_CTRL 0x001014 +#define ACP7X_PME_EN ACP70_PME_EN #endif diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 94025bc799ea..494f9cb0c06f 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -32,11 +32,20 @@ static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata) struct snd_sof_dev *sdev = adata->dev; const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); u32 swintr_trigger; - + unsigned int swintr_trigger_reg_offset; + + switch (adata->pci_rev) { + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + swintr_trigger_reg_offset = ACP7X_DSP_SW_INTR_TRIG_OFFSET; + break; + default: + swintr_trigger_reg_offset = DSP_SW_INTR_TRIG_OFFSET; + } swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->dsp_intr_base + - DSP_SW_INTR_TRIG_OFFSET); + swintr_trigger_reg_offset); swintr_trigger |= 0x01; - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_TRIG_OFFSET, + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + swintr_trigger_reg_offset, swintr_trigger); } diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index 98324bbade15..f1ec85694323 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -14,6 +14,7 @@ #include <linux/firmware.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/unaligned.h> #include "../ops.h" #include "acp-dsp-offset.h" @@ -173,10 +174,31 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) adata = sdev->pdata->hw_pdata; - if (adata->quirks && adata->quirks->signed_fw_image) + if (adata->pci_rev >= ACP7B_PCI_ID) { + if (adata->acp_sof_signed_firmware_image) { + if (adata->fw_bin_size <= ACP_IMAGE_HEADER_SIZE) { + dev_err(sdev->dev, "Invalid signed firmware size %u\n", + adata->fw_bin_size); + return -EINVAL; + } + size_fw = get_unaligned_le32(adata->bin_buf + + ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF); + if (!size_fw || + size_fw > adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE) { + dev_err(sdev->dev, + "Invalid signed firmware payload size %u (max %u)\n", + size_fw, adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE); + return -EINVAL; + } + size_fw += ACP_IMAGE_HEADER_SIZE; + } else { + size_fw = adata->fw_bin_size; + } + } else if (adata->quirks && adata->quirks->signed_fw_image) { size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; - else + } else { size_fw = adata->fw_bin_size; + } page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; adata->fw_bin_page_count = page_count; @@ -312,9 +334,14 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) } kfree(fw_filename); - ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, - (void *)adata->fw_dbin->data, - adata->fw_dbin->size); + if (adata->pci_rev >= ACP7B_PCI_ID) + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_SRAM, 0, + (void *)adata->fw_dbin->data, + adata->fw_dbin->size); + else + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, + (void *)adata->fw_dbin->data, + adata->fw_dbin->size); return ret; } EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, "SND_SOC_SOF_AMD_COMMON"); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index e6af8927baa0..f89ad86260b4 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -12,6 +12,7 @@ * Hardware interface for generic AMD ACP processor */ +#include <linux/acpi.h> #include <linux/io.h> #include <linux/module.h> #include <linux/pci.h> @@ -60,6 +61,8 @@ static void init_dma_descriptor(struct acp_dev_data *adata) case ACP70_PCI_ID: case ACP71_PCI_ID: case ACP72_PCI_ID: + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR; acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR; break; @@ -101,6 +104,8 @@ static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch, case ACP70_PCI_ID: case ACP71_PCI_ID: case ACP72_PCI_ID: + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: acp_dma_cntl_0 = ACP70_DMA_CNTL_0; acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS; acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0; @@ -283,7 +288,8 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, } } - if (adata->quirks && adata->quirks->signed_fw_image) + if ((adata->quirks && adata->quirks->signed_fw_image) || + adata->acp_sof_signed_firmware_image) snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); @@ -342,6 +348,8 @@ int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) case ACP70_PCI_ID: case ACP71_PCI_ID: case ACP72_PCI_ID: + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: acp_dma_ch_sts = ACP70_DMA_CH_STS; break; default: @@ -534,8 +542,17 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_sw0_i2s_err_reason, 0); /* ACP_SW1_I2S_ERROR_REASON is newly added register from rmb platform onwards */ - if (adata->pci_rev >= ACP_RMB_PCI_ID) + switch (adata->pci_rev) { + case ACP_RMB_PCI_ID: + case ACP63_PCI_ID: + case ACP70_PCI_ID: + case ACP71_PCI_ID: + case ACP72_PCI_ID: snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW1_I2S_ERROR_REASON, 0); + break; + default: + break; + } snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); irq_flag = 1; } @@ -564,6 +581,35 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) return IRQ_NONE; } +static irqreturn_t acp7x_irq_handler(int irq, void *dev_id) +{ + struct snd_sof_dev *sdev = dev_id; + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); + unsigned int base = desc->dsp_intr_base; + unsigned int val; + unsigned int ext_intr_stat; + int irq_flag = 0; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); + if (val & ACP_DSP_TO_HOST_IRQ) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, + ACP_DSP_TO_HOST_IRQ); + return IRQ_WAKE_THREAD; + } + + ext_intr_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); + if (ext_intr_stat & ACP_ERROR_IRQ_MASK) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); + irq_flag = 1; + } + + if (irq_flag) + return IRQ_HANDLED; + + return IRQ_NONE; +} + static int acp_power_on(struct snd_sof_dev *sdev) { const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); @@ -571,6 +617,7 @@ static int acp_power_on(struct snd_sof_dev *sdev) unsigned int base = desc->pgfsm_base; unsigned int val; unsigned int acp_pgfsm_status_mask, acp_pgfsm_cntl_mask; + bool use_masked_status = false; int ret; val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET); @@ -595,6 +642,12 @@ static int acp_power_on(struct snd_sof_dev *sdev) acp_pgfsm_status_mask = ACP70_PGFSM_STATUS_MASK; acp_pgfsm_cntl_mask = ACP70_PGFSM_CNTL_POWER_ON_MASK; break; + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + acp_pgfsm_status_mask = ACP7X_PGFSM_STATUS_MASK; + acp_pgfsm_cntl_mask = ACP7X_PGFSM_CNTL_POWER_ON_MASK; + use_masked_status = true; + break; default: return -EINVAL; } @@ -603,8 +656,17 @@ static int acp_power_on(struct snd_sof_dev *sdev) snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + PGFSM_CONTROL_OFFSET, acp_pgfsm_cntl_mask); - ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET, val, - !val, ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (use_masked_status) + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, + base + PGFSM_STATUS_OFFSET, val, + !(val & acp_pgfsm_status_mask), + ACP_REG_POLL_INTERVAL, + ACP_REG_POLL_TIMEOUT_US); + else + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, + base + PGFSM_STATUS_OFFSET, val, + !val, ACP_REG_POLL_INTERVAL, + ACP_REG_POLL_TIMEOUT_US); if (ret < 0) dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n"); @@ -703,6 +765,13 @@ static int acp_init(struct snd_sof_dev *sdev) snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1); break; + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_DSP0_IDMA_ERROR_MASK, + ACP7X_IDMA_ERROR_MASK); + break; } return 0; } @@ -749,6 +818,8 @@ int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state) case ACP72_PCI_ID: enable = true; break; + default: + break; } snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); @@ -990,6 +1061,194 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS(amd_sof_acp_remove, "SND_SOC_SOF_AMD_COMMON"); +int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = to_pci_dev(sdev->dev); + struct acp_dev_data *adata; + const struct sof_amd_acp_desc *chip; + const union acpi_object *obj; + struct acpi_device *adev; + unsigned int addr; + unsigned int irqflags; + int ret; + + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); + return -EIO; + } + adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data), GFP_KERNEL); + if (!adata) + return -ENOMEM; + + adata->dev = sdev; + adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec", + PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(adata->dmic_dev)) { + dev_err(sdev->dev, "failed to register platform for dmic codec\n"); + return PTR_ERR(adata->dmic_dev); + } + + addr = pci_resource_start(pci, ACP_DSP_BAR); + sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR)); + if (!sdev->bar[ACP_DSP_BAR]) { + dev_err(sdev->dev, "ioremap error\n"); + ret = -ENXIO; + goto unregister_dev; + } + + pci_set_master(pci); + adata->addr = addr; + adata->reg_range = chip->reg_end_addr - chip->reg_start_addr; + adata->pci_rev = pci->revision; + mutex_init(&adata->acp_lock); + sdev->pdata->hw_pdata = adata; + + ret = acp_init(sdev); + if (ret < 0) + goto unregister_dev; + + adev = ACPI_COMPANION(&pci->dev); + + sdev->ipc_irq = pci->irq; + irqflags = IRQF_SHARED; + + ret = request_threaded_irq(pci->irq, acp7x_irq_handler, acp_irq_thread, + irqflags, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to register IRQ %d\n", sdev->ipc_irq); + goto unregister_dev; + } + + if (adev) { + if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", + ACPI_TYPE_INTEGER, &obj)) + adata->acp_sof_signed_firmware_image = obj->integer.value; + } + + sdev->dsp_box.offset = 0; + sdev->dsp_box.size = BOX_SIZE_512; + + sdev->host_box.offset = sdev->dsp_box.offset + sdev->dsp_box.size; + sdev->host_box.size = BOX_SIZE_512; + + sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; + sdev->debug_box.size = BOX_SIZE_1024; + + if (adata->acp_sof_signed_firmware_image) { + adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-code.bin", chip->name); + if (!adata->fw_code_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } + adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-data.bin", chip->name); + if (!adata->fw_data_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } + } + + adata->enable_fw_debug = enable_fw_debug; + acp_memory_init(sdev); + acp_dsp_stream_init(sdev); + + return 0; + +free_ipc_irq: + free_irq(sdev->ipc_irq, sdev); +unregister_dev: + platform_device_unregister(adata->dmic_dev); + return ret; +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_probe, "SND_SOC_SOF_AMD_COMMON"); + +void amd_sof_acp7x_remove(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + + if (sdev->ipc_irq) + free_irq(sdev->ipc_irq, sdev); + + if (adata->dmic_dev) + platform_device_unregister(adata->dmic_dev); + + acp_reset(sdev); +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_remove, "SND_SOC_SOF_AMD_COMMON"); + +int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state) +{ + struct acp_dev_data *acp_data; + int ret; + bool enable = false; + + acp_data = sdev->pdata->hw_pdata; + + ret = acp_reset(sdev); + if (ret) { + dev_err(sdev->dev, "ACP Reset failed\n"); + return ret; + } + switch (acp_data->pci_rev) { + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + enable = true; + break; + default: + break; + } + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1); + + return 0; +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend, "SND_SOC_SOF_AMD_COMMON"); + +int amd_sof_acp7x_resume(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data; + int ret; + + acp_data = sdev->pdata->hw_pdata; + + ret = acp_init(sdev); + if (ret) { + dev_err(sdev->dev, "ACP Init failed\n"); + return ret; + } + ret = acp_memory_init(sdev); + if (ret) { + dev_err(sdev->dev, "ACP Memory init failed\n"); + return ret; + } + + switch (acp_data->pci_rev) { + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON"); + +int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev) +{ + return amd_sof_acp7x_suspend(sdev, 0); +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON"); + +int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev) +{ + return amd_sof_acp7x_resume(sdev); +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON"); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("AMD ACP sof driver"); MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 7bcb76676a98..16d66b2eaa70 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -76,6 +76,12 @@ #define ACP70_PCI_ID 0x70 #define ACP71_PCI_ID 0x71 #define ACP72_PCI_ID 0x72 +#define ACP7B_PCI_ID 0x7B +#define ACP7F_PCI_ID 0x7F + +#define ACP7X_PGFSM_CNTL_POWER_ON_MASK 0x7F +#define ACP7X_PGFSM_STATUS_MASK 0xFFF +#define ACP7X_SRAM_PTE_OFFSET ACP6X_SRAM_PTE_OFFSET #define HOST_BRIDGE_CZN 0x1630 #define HOST_BRIDGE_VGH 0x1645 @@ -107,6 +113,9 @@ #define PROBE_STATUS_BIT BIT(31) #define ACP_FIRMWARE_SIGNATURE 0x100 +#define ACP_IMAGE_HEADER_SIZE ACP_FIRMWARE_SIGNATURE +#define ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF 0x14 + #define ACP_ERROR_IRQ_MASK BIT(29) #define ACP_SDW0_IRQ_MASK BIT(21) #define ACP_SDW1_IRQ_MASK BIT(2) @@ -271,6 +280,7 @@ struct acp_dev_data { /* acp70_sdw1_wake_event flag set to true when wake irq asserted for SW1 instance */ bool acp70_sdw1_wake_event; unsigned int pci_rev; + int acp_sof_signed_firmware_image; }; void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); @@ -344,6 +354,16 @@ int sof_acp63_ops_init(struct snd_sof_dev *sdev); extern struct snd_sof_dsp_ops sof_acp70_ops; int sof_acp70_ops_init(struct snd_sof_dev *sdev); +extern struct snd_sof_dsp_ops sof_acp7x_ops; +int sof_acp7x_ops_init(struct snd_sof_dev *sdev); + +int amd_sof_acp7x_probe(struct snd_sof_dev *sdev); +void amd_sof_acp7x_remove(struct snd_sof_dev *sdev); +int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state); +int amd_sof_acp7x_resume(struct snd_sof_dev *sdev); +int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev); +int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev); + struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev); /* Machine configuration */ int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c new file mode 100644 index 000000000000..b6722d11168c --- /dev/null +++ b/sound/soc/sof/amd/acp7x.c @@ -0,0 +1,185 @@ +// 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) 2025 Advanced Micro Devices, Inc. +// +// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com> + +/* + * Hardware interface for Audio DSP on ACP7.B/7.F platforms + */ + +#include <linux/acpi.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <sound/soc-acpi.h> + +#include "../ops.h" +#include "../sof-audio.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +#define I2S_TDM0_INSTANCE 0 +#define I2S_TDM1_INSTANCE 1 +#define I2S_TDM2_INSTANCE 2 +#define PDM0_DMIC_INSTANCE 3 +#define PDM1_DMIC_INSTANCE 4 + +static struct snd_soc_dai_driver acp7x_sof_dai[] = { + [I2S_TDM0_INSTANCE] = { + .id = I2S_TDM0_INSTANCE, + .name = "acp-sof-i2s0", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S HS controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_TDM1_INSTANCE] = { + .id = I2S_TDM1_INSTANCE, + .name = "acp-sof-i2s1", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S BT controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_TDM2_INSTANCE] = { + .id = I2S_TDM2_INSTANCE, + .name = "acp-sof-i2s2", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S SP controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [PDM0_DMIC_INSTANCE] = { + .id = PDM0_DMIC_INSTANCE, + .name = "acp-sof-dmic0", + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [PDM1_DMIC_INSTANCE] = { + .id = PDM1_DMIC_INSTANCE, + .name = "acp-sof-dmic1", + .capture = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 96000, + }, + }, +}; + +static int sof_acp7x_post_fw_run_delay(struct snd_sof_dev *sdev) +{ + /* + * Resuming from suspend in some cases may cause the DSP firmware + * to enter an unrecoverable faulty state. Delaying a bit any host + * to DSP transmission right after firmware boot completion seems + * to resolve the issue. + */ + if (!sdev->first_boot) + usleep_range(100, 150); + + return 0; +} + +struct snd_sof_dsp_ops sof_acp7x_ops; +EXPORT_SYMBOL_NS(sof_acp7x_ops, "SND_SOC_SOF_AMD_COMMON"); + +int sof_acp7x_ops_init(struct snd_sof_dev *sdev) +{ + struct acpi_device *adev = ACPI_COMPANION(&to_pci_dev(sdev->dev)->dev); + const union acpi_object *obj; + int acp_sof_signed_firmware_image = 0; + int acp_sof_post_fw_run_delay = 0; + + /* common defaults */ + memcpy(&sof_acp7x_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); + + sof_acp7x_ops.drv = acp7x_sof_dai; + sof_acp7x_ops.num_drv = ARRAY_SIZE(acp7x_sof_dai); + sof_acp7x_ops.probe = amd_sof_acp7x_probe; + sof_acp7x_ops.remove = amd_sof_acp7x_remove; + + if (adev) { + if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", + ACPI_TYPE_INTEGER, &obj)) + acp_sof_signed_firmware_image = obj->integer.value; + + if (!acpi_dev_get_property(adev, "acp-sof-post_fw_run_delay", + ACPI_TYPE_INTEGER, &obj)) + acp_sof_post_fw_run_delay = obj->integer.value; + } + + if (acp_sof_signed_firmware_image) + sof_acp7x_ops.load_firmware = acp_sof_load_signed_firmware; + + if (acp_sof_post_fw_run_delay) + sof_acp7x_ops.post_fw_run = sof_acp7x_post_fw_run_delay; + + sof_acp7x_ops.suspend = amd_sof_acp7x_suspend; + sof_acp7x_ops.resume = amd_sof_acp7x_resume; + sof_acp7x_ops.runtime_suspend = amd_sof_acp7x_suspend_runtime; + sof_acp7x_ops.runtime_resume = amd_sof_acp7x_resume_runtime; + + return 0; +} diff --git a/sound/soc/sof/amd/pci-acp7x.c b/sound/soc/sof/amd/pci-acp7x.c new file mode 100644 index 000000000000..532e15313795 --- /dev/null +++ b/sound/soc/sof/amd/pci-acp7x.c @@ -0,0 +1,116 @@ +// 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) 2025 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com> + +/* + * PCI interface for ACP7.B/7.F devices + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <sound/sof.h> +#include <sound/soc-acpi.h> + +#include "../ops.h" +#include "../sof-pci-dev.h" +#include "../../amd/mach-config.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +#define ACP7X_FUTURE_REG_ACLK_0 0x18e0 +#define ACP7X_REG_START 0x1240000 +#define ACP7X_REG_END 0x125C000 + +static const struct sof_amd_acp_desc acp7x_chip_info = { + .name = "acp7x", + .pgfsm_base = ACP7X_PGFSM_BASE, + .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB, + .ext_intr_cntl = ACP7X_EXTERNAL_INTR_CNTL, + .ext_intr_stat = ACP7X_EXT_INTR_STAT, + .ext_intr_stat1 = ACP7X_EXT_INTR_STAT1, + .dsp_intr_base = ACP7X_DSP_SW_INTR_BASE, + .acp_error_stat = ACP7X_ERROR_STATUS, + .sram_pte_offset = ACP7X_SRAM_PTE_OFFSET, + .hw_semaphore_offset = ACP7X_AXI2DAGB_SEM_0, + .fusion_dsp_offset = ACP7X_DSP_FUSION_RUNSTALL, + .probe_reg_offset = ACP7X_FUTURE_REG_ACLK_0, + .reg_start_addr = ACP7X_REG_START, + .reg_end_addr = ACP7X_REG_END, +}; + +static const struct sof_dev_desc acp7x_desc = { + .machines = snd_soc_acpi_amd_acp7x_sof_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &acp7x_chip_info, + .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), + .ipc_default = SOF_IPC_TYPE_3, + .default_fw_path = { + [SOF_IPC_TYPE_3] = "amd/sof", + }, + .default_tplg_path = { + [SOF_IPC_TYPE_3] = "amd/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC_TYPE_3] = "sof-acp7x.ri", + }, + .nocodec_tplg_filename = "sof-acp.tplg", + .ops = &sof_acp7x_ops, + .ops_init = sof_acp7x_ops_init, +}; + +static int acp7x_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + unsigned int flag; + + switch (pci->revision) { + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + break; + default: + return -ENODEV; + } + + flag = snd_amd_acp_find_config(pci); + if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) + return -ENODEV; + + return sof_pci_probe(pci, pci_id); +} + +static void acp7x_pci_remove(struct pci_dev *pci) +{ + sof_pci_remove(pci); +} + +/* PCI IDs */ +static const struct pci_device_id acp7x_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), + .driver_data = (unsigned long)&acp7x_desc}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, acp7x_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_amd_acp7x_driver = { + .name = KBUILD_MODNAME, + .id_table = acp7x_pci_ids, + .probe = acp7x_pci_probe, + .remove = acp7x_pci_remove, + .driver = { + .pm = pm_ptr(&sof_pci_pm), + }, +}; +module_pci_driver(snd_sof_pci_amd_acp7x_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("ACP7X SOF Driver"); +MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON"); +MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c index 76bde2e0be1d..fcd57f04ca36 100644 --- a/sound/soc/sof/fw-file-profile.c +++ b/sound/soc/sof/fw-file-profile.c @@ -16,20 +16,19 @@ static int sof_test_firmware_file(struct device *dev, enum sof_ipc_type *ipc_type_to_adjust) { enum sof_ipc_type fw_ipc_type; - const struct firmware *fw; - const char *fw_filename; const u32 *magic; int ret; - fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, - profile->fw_name); + const char *fw_filename __free(kfree) = + kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, + profile->fw_name); if (!fw_filename) return -ENOMEM; + const struct firmware *fw __free(firmware) = NULL; ret = firmware_request_nowarn(&fw, fw_filename, dev); if (ret < 0) { dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename); - kfree(fw_filename); return ret; } @@ -44,8 +43,7 @@ static int sof_test_firmware_file(struct device *dev, break; default: dev_err(dev, "Invalid firmware magic: %#x\n", *magic); - ret = -EINVAL; - goto out; + return -EINVAL; } if (ipc_type_to_adjust) { @@ -54,13 +52,10 @@ static int sof_test_firmware_file(struct device *dev, dev_err(dev, "ipc type mismatch between %s and expected: %d vs %d\n", fw_filename, fw_ipc_type, profile->ipc_type); - ret = -EINVAL; + return -EINVAL; } -out: - release_firmware(fw); - kfree(fw_filename); - return ret; + return 0; } static int sof_test_topology_file(struct device *dev, diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index 8332d4bda558..aeb34310eebd 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -186,6 +186,7 @@ EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON" int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) { struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); struct hdac_stream *stream; int sd_offset, ret = 0; u32 gctl; @@ -193,6 +194,16 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) if (bus->chip_init) return 0; + /* + * The controller reset clears the ACE2+ link DMA stream allocation + * constraints; reset the masks to reflect this. + */ + memset(sof_hda->link_dma_active_sdw_mask, 0, + sizeof(sof_hda->link_dma_active_sdw_mask)); + memset(sof_hda->link_dma_active_multi_mask, 0, + sizeof(sof_hda->link_dma_active_multi_mask)); + sof_hda->link_dma_out_hda_used_mask = 0; + hda_codec_set_codec_wakeup(sdev, true); hda_dsp_ctrl_misc_clock_gating(sdev, false); @@ -223,6 +234,14 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) /* Accept unsolicited responses */ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); + /* Perform a one-time enumeration of the Multi-Link capability */ + ret = hda_bus_ml_init(bus); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to enumerate multi-links\n", + __func__); + goto err; + } + if (detect_codec) hda_codec_detect_mask(sdev); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index b2c559559962..f0be42048db3 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -20,7 +20,7 @@ /* These ops are only applicable for the HDA DAI's in their current form */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) /* - * This function checks if the host dma channel corresponding + * This function checks if the host DMA stream corresponding * to the link DMA stream_tag argument is assigned to one * of the FEs connected to the BE DAI. */ @@ -42,23 +42,53 @@ 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, + enum hda_bus_ml_link_type link_type) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); struct sof_intel_hda_stream *hda_stream; const struct sof_intel_dsp_desc *chip; struct snd_sof_dev *sdev; struct hdac_ext_stream *res = NULL; struct hdac_stream *hstream = NULL; - int stream_dir = substream->stream; + bool is_multi = link_type == HDA_BUS_ML_LINK_HDA || link_type == HDA_BUS_ML_LINK_UAOL; + bool is_play = stream_dir == SNDRV_PCM_STREAM_PLAYBACK; + bool is_sdw = link_type == HDA_BUS_ML_LINK_SDW; + bool is_hda = link_type == HDA_BUS_ML_LINK_HDA; + u32 concur_block_mask = 0; + u32 seq_block_mask = 0; + unsigned int stream_idx; if (!bus->ppcap) { dev_err(bus->dev, "stream type not supported\n"); return NULL; } + /* + * On ACE2+ the link DMA stream allocator must avoid two HW errata, + * see the comment on struct sof_intel_hda_dev. + * + * - Concurrent cross-direction: SoundWire conflicts with HDA, iDisp + * and UAOL on the same physical stream index; SSP and DMIC are safe. + * - Sequential playback: a stream index previously used by an HDA/iDisp + * link cannot drive any non-HDA/iDisp link in the same direction + * until the next controller reset. + * + * The masks are protected by bus->reg_lock; sample them inside the + * lock together with the stream walk to keep the decision atomic + * with concurrent allocations and releases. + */ guard(spinlock_irq)(&bus->reg_lock); + + if (is_sdw) + concur_block_mask = sof_hda->link_dma_active_multi_mask[!stream_dir]; + else if (is_multi) + concur_block_mask = sof_hda->link_dma_active_sdw_mask[!stream_dir]; + if (is_play && !is_hda) + seq_block_mask = sof_hda->link_dma_out_hda_used_mask; + list_for_each_entry(hstream, &bus->stream_list, list) { struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); @@ -69,6 +99,12 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream sdev = hda_stream->sdev; chip = get_chip_info(sdev->pdata); + stream_idx = hstream->stream_tag - 1; + + /* skip streams blocked by the ACE2+ allocator constraints */ + if ((concur_block_mask | seq_block_mask) & BIT(stream_idx)) + continue; + /* check if link is available */ if (!hext_stream->link_locked) { /* @@ -95,7 +131,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream /* * This must be a hostless stream. - * So reserve the host DMA channel. + * So reserve the host DMA stream. */ hda_stream->host_reserved = 1; break; @@ -109,6 +145,16 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream res->link_locked = 1; res->link_substream = substream; + + stream_idx = res->hstream.stream_tag - 1; + if (is_sdw) + sof_hda->link_dma_active_sdw_mask[stream_dir] |= BIT(stream_idx); + else if (is_multi) + sof_hda->link_dma_active_multi_mask[stream_dir] |= BIT(stream_idx); + + /* persistent OUT HDA/iDisp shadow, cleared only on CRST# */ + if (is_hda && is_play) + sof_hda->link_dma_out_hda_used_mask |= BIT(stream_idx); } return res; @@ -143,11 +189,13 @@ static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream, + struct hdac_ext_link *hlink) { struct hdac_ext_stream *hext_stream; + enum hda_bus_ml_link_type link_type = hda_bus_ml_link_get_type(hlink); - hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); + hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream, link_type); if (!hext_stream) return NULL; @@ -160,6 +208,22 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai struct snd_pcm_substream *substream) { struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); + struct sof_intel_hda_dev *sof_hda = sdev->pdata->hw_pdata; + struct hdac_bus *bus = sof_to_bus(sdev); + int dir = substream->stream; + unsigned int stream_idx = hext_stream->hstream.stream_tag - 1; + + /* + * Drop the stream index from the per-direction active concurrency masks. + * The two masks are mutually exclusive for a given stream/direction + * (and a stream of the SSP/DMIC kind appears in neither), so a blind + * clear of both is safe and lets us avoid having to remember the + * link type at allocation time. + */ + scoped_guard(spinlock_irq, &bus->reg_lock) { + sof_hda->link_dma_active_sdw_mask[dir] &= ~BIT(stream_idx); + sof_hda->link_dma_active_multi_mask[dir] &= ~BIT(stream_idx); + } snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 15faedeec16d..bb44d4f8a4da 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -188,7 +188,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, if (!hext_stream) { if (ops->assign_hext_stream) - hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream); + hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream, hlink); } if (!hext_stream) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index e9f092f082a1..b9b2bdff4ccb 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -1114,11 +1114,7 @@ static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) usleep_range(500, 1000); /* Restore state for shutdown, back to reset */ - ret = hda_dsp_ctrl_link_reset(sdev, true); - if (ret < 0) - return ret; - - return ret; + return hda_dsp_ctrl_link_reset(sdev, true); } int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 2b3abcf75d55..4347e71337f9 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -216,9 +216,10 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, - bool persistent_buffer, struct hdac_ext_stream *hext_stream) + bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax) { - return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false); + return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, + is_iccmax, false); } EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -302,7 +303,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) * If the cleanup also fails, we return the initial error */ ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab, - persistent_cl_buffer, iccmax_stream); + persistent_cl_buffer, iccmax_stream, true); if (ret1 < 0) { dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); @@ -458,7 +459,7 @@ cleanup: * If the cleanup also fails, we return the initial error */ ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, - persistent_cl_buffer, hext_stream); + persistent_cl_buffer, hext_stream, false); if (ret1 < 0) { dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); @@ -587,7 +588,7 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, cleanup: /* clean up even in case of error and return the first error */ ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer, - hext_stream); + hext_stream, false); if (ret1 < 0) { dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__); diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 92314e3b568a..6f02fb5b70ce 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -432,6 +432,10 @@ int hda_bus_ml_init(struct hdac_bus *bus) if (!bus->mlcap) return 0; + /* Enumeration is a one time operation, skip if already done */ + if (!list_empty(&bus->hlink_list)) + return 0; + link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); @@ -880,19 +884,6 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, return 0; } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK"); -void hda_bus_ml_put_all(struct hdac_bus *bus) -{ - struct hdac_ext_link *hlink; - - list_for_each_entry(hlink, &bus->hlink_list, list) { - struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); - - if (!h2link->alt) - snd_hdac_ext_bus_link_put(bus, hlink); - } -} -EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK"); - void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { struct hdac_ext_link *hlink; @@ -903,6 +894,24 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK"); +enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) +{ + struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); + + if (!h2link->alt) + return HDA_BUS_ML_LINK_HDA; + + switch (h2link->elid) { + case AZX_REG_ML_LEPTR_ID_SDW: + return HDA_BUS_ML_LINK_SDW; + case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: + return HDA_BUS_ML_LINK_UAOL; + default: + return HDA_BUS_ML_LINK_OTHER; + } +} +EXPORT_SYMBOL_NS(hda_bus_ml_link_get_type, "SND_SOC_SOF_HDA_MLINK"); + int hda_bus_ml_resume(struct hdac_bus *bus) { struct hdac_ext_link *hlink; diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c index 7351a0870725..ba9fbd662b85 100644 --- a/sound/soc/sof/intel/hda-sdw-bpt.c +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -163,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream u32 mask; int ret; - ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true); + ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, false, true); if (ret < 0) { dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", __func__); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 5c1f3b427cdb..c95230487a1b 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -198,13 +198,18 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, mask = (1 << hstream->index); + /* Reset the spib_addr before disabling SPIB */ + if (!enable) + sof_io_write(sdev, hstream->spib_addr, 0); + /* enable/disable SPIB for the stream */ snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, enable << hstream->index); /* set the SPIB value */ - sof_io_write(sdev, hstream->spib_addr, size); + if (enable) + sof_io_write(sdev, hstream->spib_addr, size); return 0; } @@ -608,6 +613,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, return ret; } + /* Host DMA is not running */ + hstream->running = false; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset + SOF_HDA_ADSP_REG_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK, @@ -1323,16 +1331,18 @@ out_put: EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, - bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair) + bool persistent_buffer, struct hdac_ext_stream *hext_stream, + bool is_iccmax, bool pair) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct hdac_stream *hstream = hdac_stream(hext_stream); int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret = 0; - if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) + if (!is_iccmax) ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); - else + + if (hstream->direction == SNDRV_PCM_STREAM_CAPTURE) snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_HDA_SD_CTL_DMA_START, 0); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index dc85903b8d46..4dbba9186b29 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -421,6 +421,20 @@ static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } /* pre fw run operations */ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + int ret; + + /* Power down DSP if left enabled to ensure a clean boot state. */ + if (hda_dsp_core_is_enabled(sdev, chip->host_managed_cores_mask)) { + dev_dbg(sdev->dev, "DSP core enabled, power down DSP first\n"); + + ret = chip->power_down_dsp(sdev); + if (ret < 0) + dev_warn(sdev->dev, + "%s: failed to power down already-enabled DSP\n", __func__); + } + /* disable clock gating and power gating */ return hda_dsp_ctrl_clock_power_gating(sdev, false); } @@ -625,8 +639,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) return ret; } - hda_bus_ml_init(bus); - /* Skip SoundWire if it is not supported */ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) goto skip_soundwire; @@ -670,8 +682,6 @@ skip_soundwire: if (!HDA_IDISP_CODEC(bus->codec_mask)) hda_codec_i915_display_power(sdev, false); - hda_bus_ml_put_all(bus); - return 0; } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 3f0966477ace..1609589929a1 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -523,6 +523,29 @@ struct sof_intel_hda_dev { /* the maximum number of streams (playback + capture) supported */ u32 stream_max; + /* + * ACE2+ link DMA stream allocation constraints (stream index = + * stream_tag - 1, shared between input and output directions). All + * masks are cleared by hda_dsp_ctrl_init_chip() on controller reset + * (CRST#). + * + * - Concurrent (cross-direction) constraint: a SoundWire stream and + * a HDA/iDisp/UAOL stream cannot share a physical stream index + * across directions, the resulting LLP/timestamp values are wrong. + * link_dma_active_sdw_mask and link_dma_active_multi_mask + * (indexed by SNDRV_PCM_STREAM_*) track currently allocated + * streams per direction in each of the conflicting groups; SSP + * and DMIC do not participate. Bits are cleared on stream release. + * + * - Sequential (playback only) constraint: once a HDA/iDisp link + * has used a playback stream index, that index cannot drive a + * non-HDA/iDisp link in the same direction until the next CRST#. + * link_dma_out_hda_used_mask records this. + */ + u32 link_dma_active_sdw_mask[SNDRV_PCM_STREAM_LAST + 1]; + u32 link_dma_active_multi_mask[SNDRV_PCM_STREAM_LAST + 1]; + u32 link_dma_out_hda_used_mask; + /* PM related */ bool l1_disabled;/* is DMI link L1 disabled? */ @@ -739,7 +762,7 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format, int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd); int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, - bool persistent_buffer, struct hdac_ext_stream *hext_stream); + bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax); int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); #define HDA_CL_STREAM_FORMAT 0x40 @@ -911,7 +934,8 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si bool is_iccmax, bool pair); int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, - bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair); + bool persistent_buffer, struct hdac_ext_stream *hext_stream, + bool is_iccmax, bool pair); /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; @@ -1030,7 +1054,8 @@ struct hda_dai_widget_dma_ops { struct snd_pcm_substream *substream); struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, - struct snd_pcm_substream *substream); + struct snd_pcm_substream *substream, + struct hdac_ext_link *hlink); void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream); void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index c1018893750c..549bb4ca73e1 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -9,10 +9,14 @@ * Hardware interface for audio DSP on IceLake. */ -#include <linux/kernel.h> -#include <linux/kconfig.h> -#include <linux/export.h> +#include <linux/array_size.h> #include <linux/bits.h> +#include <linux/dev_printk.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/types.h> + #include "../ipc4-priv.h" #include "../ops.h" #include "hda.h" diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 9503d00e6002..3d67d6777f1b 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -236,6 +236,17 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) } EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL"); +static bool mtl_dsp_is_enabled(struct snd_sof_dev *sdev) +{ + int val; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFDSSCS); + if (val & MTL_HFDSSCS_CPA_MASK) + return true; + + return false; +} + /* pre fw run operations */ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) { @@ -249,6 +260,18 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) u32 dsppwrsts; const struct sof_intel_dsp_desc *chip; + /* Power down the DSP if it is left enabled to ensure clean boot state */ + if (mtl_dsp_is_enabled(sdev)) { + dev_dbg(sdev->dev, "powering down DSP first\n"); + + ret = mtl_power_down_dsp(sdev); + if (ret < 0) { + dev_warn(sdev->dev, + "%s: failed to power down already-enabled DSP\n", __func__); + /* Continue anyway to attempt recovery */ + } + } + chip = get_chip_info(sdev->pdata); if (chip->hw_ip_version > SOF_INTEL_ACE_2_0) { dsppwrctl = PTL_HFPWRCTL2; diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 90ef5d99f626..143bf0fe8dd9 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -421,6 +421,12 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_AMD_I2S: + rate->min = private->dai_config->acp_i2s.fsync_rate; + rate->max = private->dai_config->acp_i2s.fsync_rate; + channels->min = private->dai_config->acp_i2s.tdm_slots; + channels->max = private->dai_config->acp_i2s.tdm_slots; + break; default: dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); break; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 4e066bbded91..26d85ca9be26 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -281,7 +281,10 @@ static const struct sof_topology_token acpdmic_tokens[] = { offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)}, }; -/* ACPI2S */ +/* + * ACPI2S tokens fill struct sof_ipc_dai_acp_params; SOF_DAI_AMD_I2S (ACPTDM + * on ACP7.B/7.F) reuses this tuple group rather than defining a parallel set. + */ static const struct sof_topology_token acpi2s_tokens[] = { {SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_dai_acp_params, fsync_rate)}, @@ -289,6 +292,8 @@ static const struct sof_topology_token acpi2s_tokens[] = { offsetof(struct sof_ipc_dai_acp_params, tdm_slots)}, {SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, + {SOF_TKN_AMD_ACPI2S_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_params, format)}, }; /* MICFIL PDM */ @@ -1363,6 +1368,35 @@ static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof return 0; } +static int sof_link_acp_i2s_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acp_i2s, 0, sizeof(config->acp_i2s)); + config->hdr.size = size; + + ret = sof_update_ipc_object(scomp, &config->acp_i2s, SOF_ACPI2S_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { @@ -1692,6 +1726,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) case SOF_DAI_AMD_SDW: ret = sof_link_acp_sdw_load(scomp, slink, config, dai); break; + case SOF_DAI_AMD_I2S: + ret = sof_link_acp_i2s_load(scomp, slink, config, dai); + break; default: break; } diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 6fdfb667cce8..24a1175dd690 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -163,11 +163,11 @@ static const struct sof_topology_token comp_ext_tokens[] = { {SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain, offsetof(struct snd_sof_widget, comp_domain)}, {SOF_TKN_COMP_DOMAIN_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_widget, dp_domain_id)}, + offsetof(struct snd_sof_widget, domain_id)}, {SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_widget, dp_heap_bytes)}, + offsetof(struct snd_sof_widget, heap_bytes)}, {SOF_TKN_COMP_STACK_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_widget, dp_stack_bytes)}, + offsetof(struct snd_sof_widget, stack_bytes)}, }; static const struct sof_topology_token gain_tokens[] = { @@ -406,6 +406,39 @@ sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index) return NULL; } +static void +sof_ipc4_evaluate_params_change(struct sof_ipc4_available_audio_format *available_fmt) +{ + struct sof_ipc4_audio_format *fmt; + u32 in_rate, in_channels, in_valid_bits; + u32 out_rate, out_channels, out_valid_bits; + u32 changed_params = 0; + int i, j; + + for (i = 0; i < available_fmt->num_input_formats; i++) { + fmt = &available_fmt->input_pin_fmts[i].audio_fmt; + in_rate = fmt->sampling_frequency; + in_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + in_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + for (j = 0; j < available_fmt->num_output_formats; j++) { + fmt = &available_fmt->output_pin_fmts[j].audio_fmt; + out_rate = fmt->sampling_frequency; + out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + if (in_rate != out_rate) + changed_params |= BIT(SNDRV_PCM_HW_PARAM_RATE); + if (in_channels != out_channels) + changed_params |= BIT(SNDRV_PCM_HW_PARAM_CHANNELS); + if (in_valid_bits != out_valid_bits) + changed_params |= BIT(SNDRV_PCM_HW_PARAM_FORMAT); + } + } + + available_fmt->changed_params = changed_params; +} + /** * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples * @scomp: pointer to pointer to SOC component @@ -497,6 +530,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, available_fmt->num_output_formats); } + sof_ipc4_evaluate_params_change(available_fmt); + return 0; err_out: @@ -661,6 +696,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) if (ret) goto free_copier; + /* Copier can only change format */ + available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); + /* * This callback is used by host copier and module-to-module copier, * and only host copier needs to set gtw_cfg. @@ -789,6 +827,9 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) if (ret) goto free_copier; + /* Copier can only change format */ + available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); + ret = sof_update_ipc_object(scomp, &node_type, SOF_COPIER_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(node_type), 1); @@ -1127,6 +1168,7 @@ static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget) "Invalid number of formats: input: %d, output: %d\n", src->available_fmt.num_input_formats, src->available_fmt.num_output_formats); + ret = -EINVAL; goto err; } @@ -1179,6 +1221,7 @@ static int sof_ipc4_widget_setup_comp_asrc(struct snd_sof_widget *swidget) "Invalid number of formats: input: %d, output: %d\n", asrc->available_fmt.num_input_formats, asrc->available_fmt.num_output_formats); + ret = -EINVAL; goto err; } @@ -1369,6 +1412,22 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * pipeline = pipe_widget->private; pipeline->mem_usage += total; + /* + * If this is not a Data Processing module instance, add the + * required heap sizes to the sum of all module instances belonging + * to the same pipeline, and find the maximum stack requirement + * among all module instances belonging to the same pipeline. + */ + if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { + pipe_widget->heap_bytes += swidget->heap_bytes; + if (pipe_widget->stack_bytes < swidget->stack_bytes) + pipe_widget->stack_bytes = swidget->stack_bytes; + + dev_dbg(sdev->dev, "%s mem reqs to %s heap %u stack %u", + swidget->widget->name, pipe_widget->widget->name, + pipe_widget->heap_bytes, pipe_widget->stack_bytes); + } + /* Update base_config->cpc from the module manifest */ sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); @@ -1686,6 +1745,8 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; pipeline->mem_usage = 0; + pipe_widget->heap_bytes = 0; + pipe_widget->stack_bytes = 0; if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { if (pipeline->use_chain_dma) { @@ -2063,10 +2124,57 @@ static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget } static int -sof_ipc4_prepare_copier_module(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 *pipeline_params, int dir) +sof_ipc4_copier_module_update_params(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *pipeline_params) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc4_copier_data *copier_data; + struct sof_ipc4_copier *ipc4_copier; + + switch (swidget->id) { + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + case snd_soc_dapm_buffer: + ipc4_copier = swidget->private; + copier_data = &ipc4_copier->data; + break; + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + struct snd_sof_dai *dai; + + if (pipeline->use_chain_dma) + return 0; + + dai = swidget->private; + + ipc4_copier = (struct sof_ipc4_copier *)dai->private; + copier_data = &ipc4_copier->data; + + break; + } + default: + dev_err(sdev->dev, "unsupported type %d for copier %s", + swidget->id, swidget->widget->name); + return -EINVAL; + } + + /* modify the input params for the next widget */ + return sof_ipc4_update_hw_params(sdev, pipeline_params, + &copier_data->out_format, + BIT(SNDRV_PCM_HW_PARAM_FORMAT) | + BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | + BIT(SNDRV_PCM_HW_PARAM_RATE)); +} + +static int +_sof_ipc4_prepare_copier_module(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 *pipeline_params, int dir) { struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; @@ -2538,6 +2646,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return 0; } +static int +sof_ipc4_prepare_copier_module(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 *pipeline_params, int dir) +{ + if (swidget->prepared) + return sof_ipc4_copier_module_update_params(swidget, + pipeline_params); + + return _sof_ipc4_prepare_copier_module(swidget, fe_params, + platform_params, pipeline_params, + dir); +} + static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, @@ -2551,6 +2674,10 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int input_fmt_index, output_fmt_index; + /* This cannot happen */ + if (unlikely(swidget->prepared)) + return 0; + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &gain->data.base_config, pipeline_params, @@ -2596,6 +2723,10 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int input_fmt_index, output_fmt_index; + /* Already prepared, nothing to do */ + if (swidget->prepared) + return 0; + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &mixer->base_config, pipeline_params, @@ -2642,6 +2773,10 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int output_fmt_index, input_fmt_index; + /* This cannot happen */ + if (unlikely(swidget->prepared)) + return 0; + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->data.base_config, pipeline_params, @@ -2808,6 +2943,18 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, int ret; if (available_fmt->num_input_formats) { + if (swidget->prepared) { + if (!available_fmt->num_output_formats) + return 0; + + /* modify the pipeline params with the output format */ + return sof_ipc4_update_hw_params(sdev, pipeline_params, + &process->output_format, + BIT(SNDRV_PCM_HW_PARAM_FORMAT) | + BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | + BIT(SNDRV_PCM_HW_PARAM_RATE)); + } + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &process->base_config, pipeline_params, @@ -2820,39 +2967,69 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, if (available_fmt->num_output_formats) { struct sof_ipc4_audio_format *in_fmt; struct sof_ipc4_pin_format *pin_fmt; - u32 out_ref_rate, out_ref_channels; - int out_ref_valid_bits, out_ref_type; + u32 ref_rate, ref_channels; + int ref_valid_bits, ref_type; if (available_fmt->num_input_formats) { + /* + * The process module can change parameters and their operation + * depends on the direction: + * Playback: typically they have single output format. This is + * to 'force' the conversion from input to output. + * Use the input format as reference since the single + * format is going to be picked. + * Capture: typically they have multiple output formats to + * convert from dai (input) to FE (output) parameters. + * Use the input format as base and replace the param + * which is changed by the module with the FE parameter + * Reason: we can have module which changes the + * parameters in path, we cannot use the full + * FE param set for the module output lookup. + */ in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; - out_ref_rate = in_fmt->sampling_frequency; - out_ref_channels = + ref_rate = in_fmt->sampling_frequency; + ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); - out_ref_valid_bits = + ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); - out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); + ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); } else { /* for modules without input formats, use FE params as reference */ - out_ref_rate = params_rate(fe_params); - out_ref_channels = params_channels(fe_params); + ref_rate = params_rate(fe_params); + ref_channels = params_channels(fe_params); ret = sof_ipc4_get_sample_type(sdev, fe_params); if (ret < 0) return ret; - out_ref_type = (u32)ret; + ref_type = (u32)ret; - out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); - if (out_ref_valid_bits < 0) - return out_ref_valid_bits; + ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + if (ref_valid_bits < 0) + return ref_valid_bits; } + if (dir == SNDRV_PCM_STREAM_CAPTURE) { + if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_RATE)) + ref_rate = params_rate(fe_params); + if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) + ref_channels = params_channels(fe_params); + if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) { + ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + if (ref_valid_bits < 0) + return ref_valid_bits; + + ref_type = sof_ipc4_get_sample_type(sdev, fe_params); + if (ref_type < 0) + return ref_type; + } + } output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, &process->base_config, available_fmt, - out_ref_rate, - out_ref_channels, - out_ref_valid_bits, - out_ref_type); + ref_rate, + ref_channels, + ref_valid_bits, + ref_type); if (output_fmt_index < 0) return output_fmt_index; @@ -2866,9 +3043,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, /* modify the pipeline params with the output format */ ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &process->output_format, - BIT(SNDRV_PCM_HW_PARAM_FORMAT) | - BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | - BIT(SNDRV_PCM_HW_PARAM_RATE)); + available_fmt->changed_params); if (ret) return ret; } @@ -3083,27 +3258,47 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr return 0; } -static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, - struct sof_ipc4_msg *msg, - void *ipc_data, u32 ipc_size, - void **new_data) +static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + u32 *payload, u32 *ext_pos, + struct sof_ipc4_module_init_ext_object **hdr) +{ + /* Add memory_data if comp_domain indicates DP */ + if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { + struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; + + *hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos]; + (*hdr)->header = + SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | + SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), + sizeof(u32))); + *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); + dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[*ext_pos]; + dp_mem_data->domain_id = swidget->domain_id; + dp_mem_data->stack_bytes = swidget->stack_bytes; + dp_mem_data->heap_bytes = swidget->heap_bytes; + *ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); + } +} + +static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + struct sof_ipc4_msg *msg, + void *ipc_data, u32 ipc_size, + void **new_data) { - struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; struct sof_ipc4_module_init_ext_init *ext_init; - struct sof_ipc4_module_init_ext_object *hdr; + struct sof_ipc4_module_init_ext_object *hdr = NULL; int new_size; u32 *payload; u32 ext_pos; - /* For the moment the only reason for adding init_ext_init payload is DP - * memory data. If both stack and heap size are 0 (= use default), then - * there is no need for init_ext_init payload. + /* + * Only DP widgets currently add init-ext objects here. Avoid allocating + * a max-sized payload buffer for widgets that will immediately return 0. */ - if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { - msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK; + if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) return 0; - } payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); if (!payload) @@ -3111,27 +3306,26 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, /* Add ext_init first and set objects array flag to 1 */ ext_init = (struct sof_ipc4_module_init_ext_init *)payload; - ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; ext_pos = DIV_ROUND_UP(sizeof(*ext_init), sizeof(u32)); /* Add object array objects after ext_init */ - /* Add dp_memory_data if comp_domain indicates DP */ - if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { - hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos]; - hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK | - SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | - SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), - sizeof(u32))); - ext_pos += DIV_ROUND_UP(sizeof(*hdr), sizeof(u32)); - dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[ext_pos]; - dp_mem_data->domain_id = swidget->dp_domain_id; - dp_mem_data->stack_bytes = swidget->dp_stack_bytes; - dp_mem_data->heap_bytes = swidget->dp_heap_bytes; - ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); + sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload, &ext_pos, &hdr); + + /* Add following object array items here */ + + if (!hdr) { + /* + * NOTE: Remove this early bail out, when struct + * sof_ipc4_module_init_ext_init alone has some + * function. + */ + kfree(payload); + return 0; } - /* If another array object is added, remember clear previous OBJ_LAST bit */ + ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; + hdr->header |= SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK; /* Calculate final size and check that it fits to max payload size */ new_size = ext_pos * sizeof(u32) + ipc_size; @@ -3154,6 +3348,69 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, return new_size; } +static void sof_ipc4_widget_pipe_ext_obj_memory_data(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + u32 *payload, u32 *ext_pos, + struct sof_ipc4_glb_pipe_ext_object **hdr) +{ + struct sof_ipc4_glb_pipe_ext_obj_memory_data *mem_data; + + *hdr = (struct sof_ipc4_glb_pipe_ext_object *)&payload[*ext_pos]; + (*hdr)->header = + SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA) | + SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*mem_data), + sizeof(u32))); + *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); + mem_data = (struct sof_ipc4_glb_pipe_ext_obj_memory_data *)&payload[*ext_pos]; + mem_data->domain_id = swidget->domain_id; + mem_data->stack_bytes = swidget->stack_bytes; + mem_data->heap_bytes = swidget->heap_bytes; + *ext_pos += DIV_ROUND_UP(sizeof(*mem_data), sizeof(u32)); + + dev_dbg(sdev->dev, + "%s; domain_id %u stack %u heap %u bytes", + swidget->widget->name, mem_data->domain_id, mem_data->stack_bytes, + mem_data->heap_bytes); +} + +static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + struct sof_ipc4_msg *msg, + void **new_data) +{ + struct sof_ipc4_glb_pipe_payload *payload_hdr; + struct sof_ipc4_glb_pipe_ext_object *hdr = NULL; + u32 *payload; + u32 ext_pos; + + payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + /* Add sof_ipc4_glb_pipe_payload and set array bit to 1 */ + payload_hdr = (struct sof_ipc4_glb_pipe_payload *)payload; + payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK; + ext_pos = DIV_ROUND_UP(sizeof(*payload_hdr), sizeof(u32)); + + sof_ipc4_widget_pipe_ext_obj_memory_data(sdev, swidget, payload, &ext_pos, &hdr); + /* Add following array objects here */ + + /* Mark end of object array */ + hdr->header |= SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK; + + /* Put total payload size in words to the payload header */ + payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(ext_pos); + *new_data = payload; + + /* Update msg extension bits according to the payload changes */ + msg->extension |= SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; + + dev_dbg(sdev->dev, "%s: payload word0 %#x", swidget->widget->name, + payload_hdr->word0); + + return ext_pos * sizeof(int32_t); +} + static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; @@ -3340,8 +3597,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget swidget->widget->name, swidget->pipeline_id, module_id, swidget->instance_id, swidget->core); - ret = sof_ipc4_widget_setup_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, - &ext_data); + ret = sof_ipc4_widget_mod_init_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, + &ext_data); if (ret < 0) goto fail; @@ -3353,6 +3610,17 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", swidget->widget->name, swidget->pipeline_id, swidget->instance_id, swidget->core); + + msg->extension &= ~SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; + ret = sof_ipc4_widget_pipe_create_msg_payload(sdev, swidget, msg, + &ext_data); + if (ret < 0) + goto fail; + + if (ret > 0) { + ipc_size = ret; + ipc_data = ext_data; + } } msg->data_size = ipc_size; @@ -3410,6 +3678,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget swidget->widget->name); pipeline->mem_usage = 0; + swidget->heap_bytes = 0; + swidget->stack_bytes = 0; pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; ida_free(&pipeline_ida, swidget->instance_id); swidget->instance_id = -EINVAL; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index a289c1d8f3ff..c9fe9f2e4083 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -167,7 +167,7 @@ struct sof_ipc4_pipeline { }; /** - * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data + * struct ipc4_pipeline_set_state_data - multi pipeline trigger IPC data * @count: Number of pipelines to be triggered * @pipeline_instance_ids: Flexible array of IDs of the pipelines to be triggered */ @@ -197,12 +197,15 @@ struct sof_ipc4_pin_format { * @input_pin_fmts: Available input pin formats * @num_input_formats: Number of input pin formats * @num_output_formats: Number of output pin formats + * @changed_params: Mask of changed params by the module instance between it's + * input and output formts (rate, channels, depth) */ struct sof_ipc4_available_audio_format { struct sof_ipc4_pin_format *output_pin_fmts; struct sof_ipc4_pin_format *input_pin_fmts; u32 num_input_formats; u32 num_output_formats; + u32 changed_params; }; /** @@ -210,7 +213,7 @@ struct sof_ipc4_available_audio_format { * @node_id: ID of Gateway Node * @dma_buffer_size: Preferred Gateway DMA buffer size (in bytes) * @config_length: Length of gateway node configuration blob specified in #config_data - * config_data: Gateway node configuration blob + * @config_data: Gateway node configuration blob */ struct sof_copier_gateway_cfg { uint32_t node_id; @@ -285,7 +288,9 @@ struct sof_ipc4_dma_stream_ch_map { struct sof_ipc4_dma_config { uint8_t dma_method; uint8_t pre_allocated_by_host; + /* private: */ uint16_t rsvd; + /* public: */ uint32_t dma_channel_id; uint32_t stream_id; struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map; @@ -296,7 +301,7 @@ struct sof_ipc4_dma_config { #define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000 /** - * struct sof_ipc4_dma_config: DMA configuration + * struct sof_ipc4_dma_config_tlv - DMA configuration * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size * @dma_config: actual DMA configuration @@ -321,6 +326,7 @@ struct sof_ipc4_alh_configuration_blob { * @data: IPC copier data * @copier_config: Copier + blob * @ipc_config_size: Size of copier_config + * @ipc_config_data: Copier module config data * @available_fmt: Available audio format * @frame_fmt: frame format * @msg: message structure for copier diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index c9c6c0c52c62..e77f73390bd8 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -289,6 +289,89 @@ static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, 16, 4, ipc_data, size, false); } +static const char *sof_ipc4_resource_type_str(u32 type) +{ + switch (type) { + case SOF_IPC4_MODULE_INSTANCE: + return "resource: MODULE_INSTANCE"; + case SOF_IPC4_PIPELINE: + return "resource: PIPELINE"; + case SOF_IPC4_GATEWAY: + return "resource: GATEWAY"; + case SOF_IPC4_EDF_TASK: + return "resource: EDF_TASK"; + case SOF_IPC4_INVALID_RESOURCE_TYPE: + return "Resource is invalid"; + default: + return "Unknown resource type"; + } +} + +static const char *sof_ipc4_resource_event_type_str(u32 event_type) +{ + switch (event_type) { + case SOF_IPC4_MIXER_UNDERRUN_DETECTED: + return "event: MIXER_UNDERRUN_DETECTED"; + case SOF_IPC4_PROCESS_DATA_ERROR: + return "event: PROCESS_DATA_ERROR"; + case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: + return "event: GATEWAY_UNDERRUN_DETECTED"; + case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: + return "event: GATEWAY_OVERRUN_DETECTED"; + default: + return "Unknown event type"; + } +} + +static void sof_ipc4_resource_event_handler(struct snd_sof_dev *sdev, + struct sof_ipc4_msg *ipc4_msg) +{ + struct sof_ipc4_notify_resource_data *data = ipc4_msg->data_ptr; + + /* Print event details */ + switch (data->event_type) { + case SOF_IPC4_MIXER_UNDERRUN_DETECTED: + dev_dbg(sdev->dev, "%s (%u): eos %u, mixed %u, expected %u\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type, data->data.mixer_underrun.eos_flag, + data->data.mixer_underrun.data_mixed, + data->data.mixer_underrun.expected_data_mixed); + break; + case SOF_IPC4_PROCESS_DATA_ERROR: + dev_dbg(sdev->dev, "%s (%u): error_code %#x\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type, data->data.process_data_error.error_code); + break; + case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: + case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: + dev_dbg(sdev->dev, "%s (%u)\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type); + break; + default: + dev_dbg(sdev->dev, "%s (%u): raw dws %#x %#x %#x %#x %#x %#x\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type, + data->data.dws[0], data->data.dws[1], data->data.dws[2], + data->data.dws[3], data->data.dws[4], data->data.dws[5]); + break; + } + + /* Print resource details */ + if (data->resource_type == SOF_IPC4_MODULE_INSTANCE) { + u32 module_id = SOF_IPC4_MOD_ID_GET(data->resource_id); + u32 instance_id = SOF_IPC4_MOD_INSTANCE_GET(data->resource_id); + + dev_dbg(sdev->dev, "%s (%u), module_id %u, instance_id %u\n", + sof_ipc4_resource_type_str(data->resource_type), + data->resource_type, module_id, instance_id); + } else if (data->resource_type != SOF_IPC4_INVALID_RESOURCE_TYPE) { + dev_dbg(sdev->dev, "%s (%u), id %u\n", + sof_ipc4_resource_type_str(data->resource_type), + data->resource_type, data->resource_id); + } +} + static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) { struct snd_sof_ipc_msg *msg = sdev->msg; @@ -734,6 +817,7 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) break; case SOF_IPC4_NOTIFY_RESOURCE_EVENT: data_size = sizeof(struct sof_ipc4_notify_resource_data); + handler_func = sof_ipc4_resource_event_handler; break; case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS: sof_ipc4_mtrace_update_pos(sdev, SOF_IPC4_LOG_CORE_GET(ipc4_msg->primary)); diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index 11a95dba3c9c..34744f8ae34a 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -21,12 +21,13 @@ static struct snd_soc_card sof_nocodec_card = { static int sof_nocodec_bes_setup(struct device *dev, struct snd_soc_dai_driver *drv, struct snd_soc_dai_link *links, - int link_num, struct snd_soc_card *card) + int link_num) { + struct snd_soc_card *card = &sof_nocodec_card; struct snd_soc_dai_link_component *dlc; int i; - if (!drv || !links || !card) + if (!drv || !links) return -EINVAL; /* set up BE dai_links */ @@ -67,10 +68,10 @@ static int sof_nocodec_bes_setup(struct device *dev, return 0; } -static int sof_nocodec_setup(struct device *dev, - u32 num_dai_drivers, - struct snd_soc_dai_driver *dai_drivers) +static int sof_nocodec_setup(struct device *dev, struct snd_soc_acpi_mach *mach) { + u32 num_dai_drivers = mach->mach_params.num_dai_drivers; + struct snd_soc_dai_driver *dai_drivers = mach->mach_params.dai_drivers; struct snd_soc_dai_link *links; /* create dummy BE dai_links */ @@ -78,7 +79,7 @@ static int sof_nocodec_setup(struct device *dev, if (!links) return -ENOMEM; - return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, &sof_nocodec_card); + return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers); } static int sof_nocodec_probe(struct platform_device *pdev) @@ -92,8 +93,7 @@ static int sof_nocodec_probe(struct platform_device *pdev) snd_soc_card_set_topology_name(card, "sof"); - ret = sof_nocodec_setup(card->dev, mach->mach_params.num_dai_drivers, - mach->mach_params.dai_drivers); + ret = sof_nocodec_setup(card->dev, mach); if (ret < 0) return ret; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index b2071edeaea6..f748d072109a 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -62,7 +62,7 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) * To avoid sending IPC before the previous IPC is handled, we * schedule delayed work here to call the snd_pcm_period_elapsed(). */ - schedule_work(&spcm->stream[substream->stream].period_elapsed_work); + queue_work(system_highpri_wq, &spcm->stream[substream->stream].period_elapsed_work); } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 24614e506019..72ff03b0b80d 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -501,9 +501,8 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget !sof_widget_in_same_direction(swidget, dir)) return 0; - /* skip widgets already prepared or aggregated DAI widgets*/ - if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || - is_aggregated_dai(swidget)) + /* skip widgets aggregated DAI widgets */ + if (!widget_ops[widget->id].ipc_prepare || is_aggregated_dai(swidget)) goto sink_prepare; /* prepare the source widget */ diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 138e5fcc2dd0..ae95efc9be1c 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -459,10 +459,10 @@ struct snd_sof_widget { /* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */ u32 comp_domain; - /* The values below are added to mod_init pay load if comp_domain indicates DP component */ - u32 dp_domain_id; /* DP process userspace domain ID */ - u32 dp_stack_bytes; /* DP process stack size requirement in bytes */ - u32 dp_heap_bytes; /* DP process heap size requirement in bytes */ + /* Module instance's memory configuration. */ + u32 domain_id; /* Module instance's userspace domain ID */ + u32 stack_bytes; /* Module instance's stack size requirement */ + u32 heap_bytes; /* Module instance's heap size requirement */ struct snd_soc_dapm_widget *widget; struct list_head list; /* list in sdev widget list */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 42a2d90bb705..8338133899ab 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -309,7 +309,7 @@ static const struct sof_dai_types sof_dais[] = { {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, {"MICFIL", SOF_DAI_IMX_MICFIL}, {"ACP_SDW", SOF_DAI_AMD_SDW}, - + {"ACPTDM", SOF_DAI_AMD_I2S}, }; static enum sof_ipc_dai_type find_dai(const char *name) @@ -846,6 +846,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct snd_soc_tplg_mixer_control *mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); int tlv[SOF_TLV_ITEMS]; + u32 min, max; unsigned int mask; int ret; @@ -853,6 +854,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) return -EINVAL; + min = le32_to_cpu(mc->min); + max = le32_to_cpu(mc->max); + if (min > max || max >= INT_MAX) + return -EINVAL; + /* * If control has more than 2 channels we need to override the info. This is because even if * ASoC layer has defined topology's max channel count to SND_SOC_TPLG_MAX_CHAN = 8, the @@ -863,12 +869,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, kc->info = snd_sof_volume_info; scontrol->comp_id = sdev->next_comp_id; - scontrol->min_volume_step = le32_to_cpu(mc->min); - scontrol->max_volume_step = le32_to_cpu(mc->max); + scontrol->min_volume_step = min; + scontrol->max_volume_step = max; scontrol->num_channels = le32_to_cpu(mc->num_channels); - scontrol->max = le32_to_cpu(mc->max); - if (le32_to_cpu(mc->max) == 1) + scontrol->max = max; + if (max == 1) goto skip; /* extract tlv data */ @@ -878,7 +884,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, } /* set up volume table */ - ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); + ret = set_up_volume_table(scontrol, tlv, max + 1); if (ret < 0) { dev_err(scomp->dev, "error: setting up volume table\n"); return ret; @@ -911,7 +917,7 @@ skip: return 0; err: - if (le32_to_cpu(mc->max) > 1) + if (max > 1) kfree(scontrol->volume_table); return ret; @@ -1102,7 +1108,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, full = NULL; partial = NULL; - list_for_each_entry(rtd, &card->rtd_list, list) { + for_each_card_rtds(card, rtd) { /* does stream match DAI link ? */ if (rtd->dai_link->stream_name) { if (!strcmp(rtd->dai_link->stream_name, w->sname)) { @@ -1167,7 +1173,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, else return; - list_for_each_entry(rtd, &card->rtd_list, list) { + for_each_card_rtds(card, rtd) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || !strstr(rtd->dai_link->stream_name, sname)) @@ -1943,8 +1949,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ private->array, le32_to_cpu(private->size)); if (ret < 0) { dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); - kfree(slink); - return ret; + goto free_slink; } token_list = tplg_ops ? tplg_ops->token_list : NULL; @@ -1995,6 +2000,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ case SOF_DAI_AMD_HS: case SOF_DAI_AMD_SP_VIRTUAL: case SOF_DAI_AMD_HS_VIRTUAL: + case SOF_DAI_AMD_I2S: token_id = SOF_ACPI2S_TOKENS; num_tuples += token_list[SOF_ACPI2S_TOKENS].count; break; @@ -2013,8 +2019,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ /* allocate memory for tuples array */ slink->tuples = kzalloc_objs(*slink->tuples, num_tuples); if (!slink->tuples) { - kfree(slink); - return -ENOMEM; + ret = -ENOMEM; + goto free_slink; } if (token_list[SOF_DAI_LINK_TOKENS].tokens) { @@ -2070,6 +2076,7 @@ out: err: kfree(slink->tuples); +free_slink: kfree(slink); return ret; @@ -2505,13 +2512,12 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_pdata *sof_pdata = sdev->pdata; const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; - const struct firmware *fw; - const char **tplg_files; int tplg_cnt = 0; int ret; int i; - tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); + const char **tplg_files __free(kfree) = + kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); if (!tplg_files) return -ENOMEM; @@ -2537,10 +2543,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) tplg_filename_prefix, &tplg_files, no_fallback); - if (tplg_cnt < 0) { - kfree(tplg_files); + if (tplg_cnt < 0) return tplg_cnt; - } } /* @@ -2551,8 +2555,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) if (strstr(file, "dummy")) { dev_err(scomp->dev, "Function topology is required, please upgrade sof-firmware\n"); - - kfree(tplg_files); return -EINVAL; } tplg_files[0] = file; @@ -2567,6 +2569,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) if (tplg_files[0] != file) dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]); + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, tplg_files[i], scomp->dev); if (ret < 0) { /* @@ -2585,8 +2588,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) else ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); - release_firmware(fw); - if (ret < 0) { dev_err(scomp->dev, "tplg %s component load failed %d\n", tplg_files[i], ret); @@ -2605,6 +2606,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) goto out; } dev_info(scomp->dev, "loading feature topology %d: %s\n", i, feature_topology); + + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, feature_topology, scomp->dev); if (ret < 0) { /* @@ -2629,8 +2632,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) else ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); - release_firmware(fw); - if (ret < 0) { dev_err(scomp->dev, "feature tplg %s component load failed %d\n", feature_topologies[i], ret); @@ -2649,8 +2650,6 @@ out: if (ret >= 0 && sdev->led_present) ret = snd_ctl_led_request(); - kfree(tplg_files); - return ret; } EXPORT_SYMBOL(snd_sof_load_topology); diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c index 4cbac8c1160f..4badb6e3e08b 100644 --- a/sound/soc/sophgo/cv1800b-tdm.c +++ b/sound/soc/sophgo/cv1800b-tdm.c @@ -558,7 +558,7 @@ static const struct snd_soc_dai_ops cv1800b_i2s_dai_ops = { .set_sysclk = cv1800b_i2s_dai_set_sysclk, }; -static const struct snd_soc_dai_driver cv1800b_i2s_dai_template = { +static struct snd_soc_dai_driver cv1800b_i2s_dai_template = { .name = "cv1800b-i2s", .playback = { .stream_name = "Playback", @@ -636,7 +636,6 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) struct cv1800b_i2s *i2s; struct resource *res; void __iomem *regs; - struct snd_soc_dai_driver *dai; int ret; i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); @@ -666,21 +665,14 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2s); cv1800b_i2s_setup_tdm(i2s); - dai = devm_kmemdup(dev, &cv1800b_i2s_dai_template, sizeof(*dai), - GFP_KERNEL); - if (!dai) - return -ENOMEM; - - ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, dai, - 1); + ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, + &cv1800b_i2s_dai_template, 1); if (ret) return ret; ret = devm_snd_dmaengine_pcm_register(dev, &cv1800b_i2s_pcm_config, 0); - if (ret) { - dev_err(dev, "dmaengine_pcm_register failed: %d\n", ret); + if (ret) return ret; - } return 0; } diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c index 8871fc15b29c..28a762769253 100644 --- a/sound/soc/spacemit/k1_i2s.c +++ b/sound/soc/spacemit/k1_i2s.c @@ -52,7 +52,7 @@ struct spacemit_i2s_dev { struct clk *sysclk; struct clk *bclk; - struct clk *sspa_clk; + struct clk *func_clk; struct clk *sysclk_div; struct clk *c_sysclk; struct clk *c_bclk; @@ -221,7 +221,7 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; - return clk_set_rate(i2s->sspa_clk, bclk_rate); + return clk_set_rate(i2s->func_clk, bclk_rate); } static int spacemit_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, @@ -335,6 +335,11 @@ static int spacemit_i2s_dai_remove(struct snd_soc_dai *dai) return 0; } +static const u64 spacemit_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B; + static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { .probe = spacemit_i2s_dai_probe, .remove = spacemit_i2s_dai_remove, @@ -343,26 +348,12 @@ static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { .set_sysclk = spacemit_i2s_set_sysclk, .set_fmt = spacemit_i2s_set_fmt, .trigger = spacemit_i2s_trigger, + .auto_selectable_formats = &spacemit_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver spacemit_i2s_dai = { .ops = &spacemit_i2s_dai_ops, - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = SPACEMIT_PCM_RATES, - .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_48000, - .formats = SPACEMIT_PCM_FORMATS, - }, - .capture = { - .channels_min = 1, - .channels_max = 2, - .rates = SPACEMIT_PCM_RATES, - .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_48000, - .formats = SPACEMIT_PCM_FORMATS, - }, .symmetric_rate = 1, }; @@ -392,6 +383,8 @@ static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s, dai->playback.channels_min = 1; dai->playback.channels_max = 2; dai->playback.rates = SPACEMIT_PCM_RATES; + dai->playback.rate_min = SNDRV_PCM_RATE_8000; + dai->playback.rate_max = SNDRV_PCM_RATE_48000; dai->playback.formats = SPACEMIT_PCM_FORMATS; i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; @@ -404,6 +397,8 @@ static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s, dai->capture.channels_min = 1; dai->capture.channels_max = 2; dai->capture.rates = SPACEMIT_PCM_RATES; + dai->capture.rate_min = SNDRV_PCM_RATE_8000; + dai->capture.rate_max = SNDRV_PCM_RATE_48000; dai->capture.formats = SPACEMIT_PCM_FORMATS; i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; @@ -445,14 +440,14 @@ static int spacemit_i2s_probe(struct platform_device *pdev) if (IS_ERR(i2s->bclk)) return dev_err_probe(i2s->dev, PTR_ERR(i2s->bclk), "failed to enable bit clock\n"); - clk = devm_clk_get_enabled(i2s->dev, "sspa_bus"); + clk = devm_clk_get_enabled(i2s->dev, "bus"); if (IS_ERR(clk)) - return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable sspa_bus clock\n"); + return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable bus clock\n"); - i2s->sspa_clk = devm_clk_get_enabled(i2s->dev, "sspa"); - if (IS_ERR(i2s->sspa_clk)) - return dev_err_probe(i2s->dev, PTR_ERR(i2s->sspa_clk), - "failed to enable sspa clock\n"); + i2s->func_clk = devm_clk_get_enabled(i2s->dev, "func"); + if (IS_ERR(i2s->func_clk)) + return dev_err_probe(i2s->dev, PTR_ERR(i2s->func_clk), + "failed to enable func clock\n"); i2s->sysclk_div = devm_clk_get_optional_enabled(i2s->dev, "sysclk_div"); if (IS_ERR(i2s->sysclk_div)) @@ -471,7 +466,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2s->base)) - return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n"); + return PTR_ERR(i2s->base); i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(i2s->reset)) @@ -488,7 +483,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) &spacemit_i2s_component, dai, 1); if (ret) - return dev_err_probe(i2s->dev, ret, "failed to register component"); + return ret; return devm_snd_dmaengine_pcm_register(&pdev->dev, &spacemit_dmaengine_pcm_config, 0); } diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index b31b120a85de..dd1e176193af 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -55,7 +55,7 @@ static int spdif_in_dai_probe(struct snd_soc_dai *dai) struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); host->dma_params_rx.filter_data = &host->dma_params; - dai->capture_dma_data = &host->dma_params_rx; + snd_soc_dai_dma_data_set_capture(dai, &host->dma_params_rx); return 0; } @@ -150,12 +150,12 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, static const struct snd_soc_dai_ops spdif_in_dai_ops = { .shutdown = spdif_in_shutdown, + .probe = spdif_in_dai_probe, .trigger = spdif_in_trigger, .hw_params = spdif_in_hw_params, }; static struct snd_soc_dai_driver spdif_in_dai = { - .probe = spdif_in_dai_probe, .capture = { .channels_min = 2, .channels_max = 2, @@ -218,10 +218,8 @@ static int spdif_in_probe(struct platform_device *pdev) host->io_base = io_base; host->irq = platform_get_irq(pdev, 0); - if (host->irq < 0) { - dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq); + if (host->irq < 0) return host->irq; - } host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) @@ -243,10 +241,8 @@ static int spdif_in_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, "spdif-in", host); - if (ret) { - dev_warn(&pdev->dev, "request_irq failed\n"); + if (ret) return ret; - } ret = devm_snd_soc_register_component(&pdev->dev, &spdif_in_component, &spdif_in_dai, 1); diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index c06f09c646a8..01dc2cef7448 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -249,6 +249,7 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops spdif_out_dai_ops = { + .probe = spdif_soc_dai_probe, .mute_stream = spdif_mute, .startup = spdif_out_startup, .shutdown = spdif_out_shutdown, @@ -266,7 +267,6 @@ static struct snd_soc_dai_driver spdif_out_dai = { SNDRV_PCM_RATE_192000), .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .probe = spdif_soc_dai_probe, .ops = &spdif_out_dai_ops, }; diff --git a/sound/soc/sprd/sprd-mcdt.c b/sound/soc/sprd/sprd-mcdt.c index 5f3a2a7bce31..d9a7bbd6c0b6 100644 --- a/sound/soc/sprd/sprd-mcdt.c +++ b/sound/soc/sprd/sprd-mcdt.c @@ -931,10 +931,8 @@ static int sprd_mcdt_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, sprd_mcdt_irq_handler, 0, "sprd-mcdt", mcdt); - if (ret) { - dev_err(&pdev->dev, "Failed to request MCDT IRQ\n"); + if (ret) return ret; - } sprd_mcdt_init_chans(mcdt, res); diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c index cbf5bf82d96e..f509a4601de2 100644 --- a/sound/soc/sprd/sprd-pcm-dma.c +++ b/sound/soc/sprd/sprd-pcm-dma.c @@ -469,8 +469,6 @@ static int sprd_soc_platform_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &sprd_soc_component, NULL, 0); - if (ret) - dev_err(&pdev->dev, "could not register platform:%d\n", ret); return ret; } diff --git a/sound/soc/starfive/jh7110_pwmdac.c b/sound/soc/starfive/jh7110_pwmdac.c index a603dd17931c..5953c3a4e373 100644 --- a/sound/soc/starfive/jh7110_pwmdac.c +++ b/sound/soc/starfive/jh7110_pwmdac.c @@ -477,25 +477,22 @@ static int jh7110_pwmdac_probe(struct platform_device *pdev) &jh7110_pwmdac_component, &jh7110_pwmdac_dai, 1); if (ret) - return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); + return ret; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) - return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); + return ret; pm_runtime_enable(dev->dev); if (!pm_runtime_enabled(&pdev->dev)) { ret = jh7110_pwmdac_runtime_resume(&pdev->dev); - if (ret) - goto err_pm_disable; + if (ret) { + pm_runtime_disable(&pdev->dev); + return ret; + } } return 0; - -err_pm_disable: - pm_runtime_disable(&pdev->dev); - - return ret; } static void jh7110_pwmdac_remove(struct platform_device *pdev) diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c index afdcde7df91a..f7522bfbe8e6 100644 --- a/sound/soc/starfive/jh7110_tdm.c +++ b/sound/soc/starfive/jh7110_tdm.c @@ -559,10 +559,8 @@ static int jh7110_tdm_clk_reset_get(struct platform_device *pdev, tdm->clks[5].id = "tdm"; ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks); - if (ret) { - dev_err(&pdev->dev, "Failed to get tdm clocks\n"); + if (ret) return ret; - } tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev); if (IS_ERR(tdm->resets)) { @@ -589,42 +587,33 @@ static int jh7110_tdm_probe(struct platform_device *pdev) tdm->dev = &pdev->dev; ret = jh7110_tdm_clk_reset_get(pdev, tdm); - if (ret) { - dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n"); + if (ret) return ret; - } jh7110_tdm_init_params(tdm); dev_set_drvdata(&pdev->dev, tdm); ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component, &jh7110_tdm_dai, 1); - if (ret) { - dev_err(&pdev->dev, "Failed to register dai\n"); + if (ret) return ret; - } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &jh7110_dmaengine_pcm_config, SND_DMAENGINE_PCM_FLAG_COMPAT); - if (ret) { - dev_err(&pdev->dev, "Could not register pcm: %d\n", ret); + if (ret) return ret; - } pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { ret = jh7110_tdm_runtime_resume(&pdev->dev); - if (ret) - goto err_pm_disable; + if (ret) { + pm_runtime_disable(&pdev->dev); + return ret; + } } return 0; - -err_pm_disable: - pm_runtime_disable(&pdev->dev); - - return ret; } static void jh7110_tdm_dev_remove(struct platform_device *pdev) diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 66efb9a0acb9..73c6460a6327 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -360,11 +360,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &stm32_adfsdm_soc_platform, NULL, 0); - if (ret < 0) { - dev_err(&pdev->dev, "%s: Failed to register PCM platform\n", - __func__); + if (ret < 0) return ret; - } pm_runtime_enable(&pdev->dev); diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index ae9e25657f3f..83b51893b37c 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -1238,10 +1238,8 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, 0, dev_name(&pdev->dev), i2s); - if (ret) { - dev_err(&pdev->dev, "irq request returned %d\n", ret); + if (ret) return ret; - } /* Reset */ rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); @@ -1295,7 +1293,7 @@ static int stm32_i2s_probe(struct platform_device *pdev) ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); if (ret) - return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); + return ret; ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, i2s->dai_drv, 1); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index ea9e8bddd63f..9a201bdb306f 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1696,19 +1696,15 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, IRQF_SHARED, dev_name(&pdev->dev), sai); - if (ret) { - dev_err(&pdev->dev, "IRQ request returned %d\n", ret); + if (ret) goto err_unprepare_pclk; - } if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) conf = &stm32_sai_pcm_config_spdif; ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); - if (ret) { - ret = dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); + if (ret) goto err_unprepare_pclk; - } ret = snd_soc_register_component(&pdev->dev, &stm32_component, &sai->cpu_dai_drv, 1); diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 2f83ca989e68..e0fef47a227e 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -970,10 +970,8 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, dev_name(&pdev->dev), spdifrx); - if (ret) { - dev_err(&pdev->dev, "IRQ request returned %d\n", ret); + if (ret) return ret; - } rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); if (IS_ERR(rst)) @@ -987,7 +985,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) pcm_config = &stm32_spdifrx_pcm_config; ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); if (ret) - return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); + return ret; ret = snd_soc_register_component(&pdev->dev, &stm32_spdifrx_component, diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 93f511fb6190..ff56700a9fe9 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -22,13 +22,13 @@ #include <linux/gpio/consumer.h> #include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> #include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/tlv.h> -#include <sound/initval.h> -#include <sound/dmaengine_pcm.h> /* Codec DAC digital controls and FIFO registers */ #define SUN4I_CODEC_DAC_DPC (0x00) @@ -2317,67 +2317,54 @@ static int sun4i_codec_probe(struct platform_device *pdev) scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, quirks->regmap_config); - if (IS_ERR(scodec->regmap)) { - dev_err(&pdev->dev, "Failed to create our regmap\n"); - return PTR_ERR(scodec->regmap); - } + if (IS_ERR(scodec->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->regmap), + "Failed to create our regmap\n"); /* Get the clocks from the DT */ scodec->clk_apb = devm_clk_get_enabled(&pdev->dev, "apb"); - if (IS_ERR(scodec->clk_apb)) { - dev_err(&pdev->dev, "Failed to get the APB clock\n"); - return PTR_ERR(scodec->clk_apb); - } + if (IS_ERR(scodec->clk_apb)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_apb), + "Failed to get the APB clock\n"); scodec->clk_module = devm_clk_get(&pdev->dev, "codec"); - if (IS_ERR(scodec->clk_module)) { - dev_err(&pdev->dev, "Failed to get the module clock\n"); - return PTR_ERR(scodec->clk_module); - } + if (IS_ERR(scodec->clk_module)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_module), + "Failed to get the module clock\n"); if (quirks->has_reset) { scodec->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); - if (IS_ERR(scodec->rst)) { - dev_err(&pdev->dev, "Failed to get reset control\n"); - return PTR_ERR(scodec->rst); - } + if (IS_ERR(scodec->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->rst), + "Failed to get reset control\n"); } scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", GPIOD_OUT_LOW); - if (IS_ERR(scodec->gpio_pa)) { - ret = PTR_ERR(scodec->gpio_pa); - dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); - return ret; - } + if (IS_ERR(scodec->gpio_pa)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_pa), + "Failed to get pa gpio\n"); + scodec->gpio_hp = devm_gpiod_get_optional(&pdev->dev, "hp-det", GPIOD_IN); - if (IS_ERR(scodec->gpio_hp)) { - ret = PTR_ERR(scodec->gpio_hp); - dev_err_probe(&pdev->dev, ret, "Failed to get hp-det gpio\n"); - return ret; - } + if (IS_ERR(scodec->gpio_hp)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_hp), + "Failed to get hp-det gpio\n"); /* reg_field setup */ scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, scodec->regmap, quirks->reg_adc_fifoc); - if (IS_ERR(scodec->reg_adc_fifoc)) { - ret = PTR_ERR(scodec->reg_adc_fifoc); - dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", - ret); - return ret; - } + if (IS_ERR(scodec->reg_adc_fifoc)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_adc_fifoc), + "Failed to create regmap fields\n"); scodec->reg_dac_fifoc = devm_regmap_field_alloc(&pdev->dev, scodec->regmap, quirks->reg_dac_fifoc); - if (IS_ERR(scodec->reg_dac_fifoc)) { - ret = PTR_ERR(scodec->reg_dac_fifoc); - dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", - ret); - return ret; - } + if (IS_ERR(scodec->reg_dac_fifoc)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_dac_fifoc), + "Failed to create regmap fields\n"); /* DMA configuration for TX FIFO */ scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; @@ -2394,39 +2381,29 @@ static int sun4i_codec_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec, &sun4i_codec_dai, 1); - if (ret) { - dev_err(&pdev->dev, "Failed to register our codec\n"); + if (ret) return ret; - } ret = devm_snd_soc_register_component(&pdev->dev, &sun4i_codec_component, &dummy_cpu_dai, 1); - if (ret) { - dev_err(&pdev->dev, "Failed to register our DAI\n"); + if (ret) return ret; - } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); + if (ret) return ret; - } card = quirks->create_card(&pdev->dev); - if (IS_ERR(card)) { - ret = PTR_ERR(card); - dev_err(&pdev->dev, "Failed to create our card\n"); - return ret; - } + if (IS_ERR(card)) + return PTR_ERR(card); snd_soc_card_set_drvdata(card, scodec); ret = snd_soc_register_card(card); - if (ret) { - dev_err_probe(&pdev->dev, ret, "Failed to register our card\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register our card\n"); return 0; } diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 40de99a34bc3..716fa4d872ff 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -1550,30 +1550,26 @@ static int sun4i_i2s_probe(struct platform_device *pdev) } i2s->bus_clk = devm_clk_get(&pdev->dev, "apb"); - if (IS_ERR(i2s->bus_clk)) { - dev_err(&pdev->dev, "Can't get our bus clock\n"); - return PTR_ERR(i2s->bus_clk); - } + if (IS_ERR(i2s->bus_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->bus_clk), + "Can't get our bus clock\n"); i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, i2s->variant->sun4i_i2s_regmap); - if (IS_ERR(i2s->regmap)) { - dev_err(&pdev->dev, "Regmap initialisation failed\n"); - return PTR_ERR(i2s->regmap); - } + if (IS_ERR(i2s->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), + "Regmap initialisation failed\n"); i2s->mod_clk = devm_clk_get(&pdev->dev, "mod"); - if (IS_ERR(i2s->mod_clk)) { - dev_err(&pdev->dev, "Can't get our mod clock\n"); - return PTR_ERR(i2s->mod_clk); - } + if (IS_ERR(i2s->mod_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mod_clk), + "Can't get our mod clock\n"); if (i2s->variant->has_reset) { i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(i2s->rst)) { - dev_err(&pdev->dev, "Failed to get reset control\n"); - return PTR_ERR(i2s->rst); - } + if (IS_ERR(i2s->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), + "Failed to get reset control\n"); } if (!IS_ERR(i2s->rst)) { diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index c2ec19437cd7..d979bb00312f 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -684,26 +684,23 @@ static int sun4i_spdif_probe(struct platform_device *pdev) host->regmap = devm_regmap_init_mmio(&pdev->dev, base, &sun4i_spdif_regmap_config); - if (IS_ERR(host->regmap)) { - dev_err(&pdev->dev, "failed to initialise regmap.\n"); - return PTR_ERR(host->regmap); - } + if (IS_ERR(host->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->regmap), + "failed to initialise regmap.\n"); /* Clocks */ host->apb_clk = devm_clk_get(&pdev->dev, "apb"); - if (IS_ERR(host->apb_clk)) { - dev_err(&pdev->dev, "failed to get a apb clock.\n"); - return PTR_ERR(host->apb_clk); - } + if (IS_ERR(host->apb_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->apb_clk), + "failed to get a apb clock.\n"); if (quirks->tx_clk_name) tx_clk_name = quirks->tx_clk_name; host->spdif_clk = devm_clk_get(&pdev->dev, tx_clk_name); - if (IS_ERR(host->spdif_clk)) { - dev_err(&pdev->dev, "failed to get the \"%s\" clock.\n", - tx_clk_name); - return PTR_ERR(host->spdif_clk); - } + if (IS_ERR(host->spdif_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->spdif_clk), + "failed to get the \"%s\" clock.\n", + tx_clk_name); host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; host->dma_params_tx.maxburst = 8; diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index 9f5b067a8ccc..50c0dee8c433 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -551,16 +551,13 @@ static int sun50i_codec_analog_probe(struct platform_device *pdev) bool enable; base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) { - dev_err(&pdev->dev, "Failed to map the registers\n"); + if (IS_ERR(base)) return PTR_ERR(base); - } regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); - if (IS_ERR(regmap)) { - dev_err(&pdev->dev, "Failed to create regmap\n"); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(regmap), + "Failed to create regmap\n"); enable = device_property_read_bool(&pdev->dev, "allwinner,internal-bias-resistor"); diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c index 2024e2b2553e..3335855c6311 100644 --- a/sound/soc/sunxi/sun8i-codec-analog.c +++ b/sound/soc/sunxi/sun8i-codec-analog.c @@ -822,16 +822,13 @@ static int sun8i_codec_analog_probe(struct platform_device *pdev) void __iomem *base; base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) { - dev_err(&pdev->dev, "Failed to map the registers\n"); + if (IS_ERR(base)) return PTR_ERR(base); - } regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); - if (IS_ERR(regmap)) { - dev_err(&pdev->dev, "Failed to create regmap\n"); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(regmap), + "Failed to create regmap\n"); return devm_snd_soc_register_component(&pdev->dev, &sun8i_codec_analog_cmpnt_drv, diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c index b48cc4a6967b..ea8bc9bcfa67 100644 --- a/sound/soc/tegra/tegra20_das.c +++ b/sound/soc/tegra/tegra20_das.c @@ -167,10 +167,9 @@ static int tegra20_das_probe(struct platform_device *pdev) das->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &tegra20_das_regmap_config); - if (IS_ERR(das->regmap)) { - dev_err(&pdev->dev, "regmap init failed\n"); - return PTR_ERR(das->regmap); - } + if (IS_ERR(das->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(das->regmap), + "regmap init failed\n"); tegra20_das_connect_dap_to_dac(das, TEGRA20_DAS_DAP_ID_1, TEGRA20_DAS_DAP_SEL_DAC1); diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c index 15a94196ee1a..e75bf50f8fb0 100644 --- a/sound/soc/tegra/tegra210_adx.c +++ b/sound/soc/tegra/tegra210_adx.c @@ -496,16 +496,12 @@ static struct snd_kcontrol_new tegra264_adx_controls[] = { static int tegra210_adx_component_probe(struct snd_soc_component *component) { struct tegra210_adx *adx = snd_soc_component_get_drvdata(component); - int err = 0; - if (adx->soc_data->num_controls) { - err = snd_soc_add_component_controls(component, adx->soc_data->controls, - adx->soc_data->num_controls); - if (err) - dev_err(component->dev, "can't add ADX controls, err: %d\n", err); - } + if (adx->soc_data->num_controls) + return snd_soc_add_component_controls(component, adx->soc_data->controls, + adx->soc_data->num_controls); - return err; + return 0; } static const struct snd_soc_component_driver tegra210_adx_cmpnt = { diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c index cc1f9c158191..6a67ce6128b4 100644 --- a/sound/soc/tegra/tegra210_amx.c +++ b/sound/soc/tegra/tegra210_amx.c @@ -504,16 +504,12 @@ static struct snd_kcontrol_new tegra264_amx_controls[] = { static int tegra210_amx_component_probe(struct snd_soc_component *component) { struct tegra210_amx *amx = snd_soc_component_get_drvdata(component); - int err = 0; - if (amx->soc_data->num_controls) { - err = snd_soc_add_component_controls(component, amx->soc_data->controls, - amx->soc_data->num_controls); - if (err) - dev_err(component->dev, "can't add AMX controls, err: %d\n", err); - } + if (amx->soc_data->num_controls) + return snd_soc_add_component_controls(component, amx->soc_data->controls, + amx->soc_data->num_controls); - return err; + return 0; } static const struct snd_soc_component_driver tegra210_amx_cmpnt = { diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index ff8c72fc38c5..84506576437d 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -23,9 +23,9 @@ static const struct reg_default tegra210_i2s_reg_defaults[] = { { TEGRA210_I2S_RX_CIF_CTRL, 0x00007700 }, { TEGRA210_I2S_TX_INT_MASK, 0x00000003 }, { TEGRA210_I2S_TX_CIF_CTRL, 0x00007700 }, + { TEGRA210_I2S_ENABLE, 0x1 }, { TEGRA210_I2S_CG, 0x1 }, { TEGRA210_I2S_TIMING, 0x0000001f }, - { TEGRA210_I2S_ENABLE, 0x1 }, /* * Below update does not have any effect on Tegra186 and Tegra194. * On Tegra210, I2S4 has "i2s4a" and "i2s4b" pins and below update @@ -38,13 +38,13 @@ static const struct reg_default tegra210_i2s_reg_defaults[] = { static const struct reg_default tegra264_i2s_reg_defaults[] = { { TEGRA210_I2S_RX_INT_MASK, 0x00000003 }, { TEGRA210_I2S_RX_CIF_CTRL, 0x00003f00 }, + { TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE, 0x1 }, { TEGRA264_I2S_TX_INT_MASK, 0x00000003 }, { TEGRA264_I2S_TX_CIF_CTRL, 0x00003f00 }, + { TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE, 0x1 }, + { TEGRA264_I2S_ENABLE, 0x1 }, { TEGRA264_I2S_CG, 0x1 }, { TEGRA264_I2S_TIMING, 0x0000001f }, - { TEGRA264_I2S_ENABLE, 0x1 }, - { TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE, 0x1 }, - { TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE, 0x1 }, }; static void tegra210_i2s_set_slot_ctrl(struct tegra210_i2s *i2s, @@ -1078,13 +1078,13 @@ static int tegra210_i2s_probe(struct platform_device *pdev) "can't retrieve I2S bit clock\n"); /* - * Not an error, as this clock is needed only when some other I/O - * requires input clock from current I2S instance, which is - * configurable from DT. + * This clock is optional and is only needed when another I/O uses + * the current I2S instance as its input clock, as configured in DT. */ - i2s->clk_sync_input = devm_clk_get(dev, "sync_input"); + i2s->clk_sync_input = devm_clk_get_optional(dev, "sync_input"); if (IS_ERR(i2s->clk_sync_input)) - dev_dbg(dev, "can't retrieve I2S sync input clock\n"); + return dev_err_probe(dev, PTR_ERR(i2s->clk_sync_input), + "can't retrieve I2S sync input clock\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c index a69774578d69..8eb4e54b954b 100644 --- a/sound/soc/tegra/tegra210_mixer.c +++ b/sound/soc/tegra/tegra210_mixer.c @@ -57,10 +57,10 @@ static const struct reg_default tegra210_mixer_reg_defaults[] = { MIXER_TX_REG_DEFAULTS(3), MIXER_TX_REG_DEFAULTS(4), + { TEGRA210_MIXER_ENABLE, 0x1 }, { TEGRA210_MIXER_CG, 0x00000001}, { TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000}, { TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000}, - { TEGRA210_MIXER_ENABLE, 0x1 }, }; /* Default gain parameters */ diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c index 61252359d5cb..b4f6be2ab4c8 100644 --- a/sound/soc/ti/ams-delta.c +++ b/sound/soc/ti/ams-delta.c @@ -562,21 +562,21 @@ static int ams_delta_probe(struct platform_device *pdev) card->dev = &pdev->dev; - handset_mute = devm_gpiod_get(card->dev, "handset_mute", + handset_mute = devm_gpiod_get(&pdev->dev, "handset_mute", GPIOD_OUT_HIGH); if (IS_ERR(handset_mute)) return PTR_ERR(handset_mute); - handsfree_mute = devm_gpiod_get(card->dev, "handsfree_mute", + handsfree_mute = devm_gpiod_get(&pdev->dev, "handsfree_mute", GPIOD_OUT_HIGH); if (IS_ERR(handsfree_mute)) return PTR_ERR(handsfree_mute); ret = snd_soc_register_card(card); if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); card->dev = NULL; - return ret; + return dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card() failed\n"); } return 0; } diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c index ad514c2e5a25..3156d87a3a12 100644 --- a/sound/soc/ti/davinci-evm.c +++ b/sound/soc/ti/davinci-evm.c @@ -245,9 +245,8 @@ static int davinci_evm_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(&evm_soc_card, drvdata); ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); goto err_put; } diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index c214ae0d7b95..600897a3e1c5 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -4,6 +4,7 @@ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> */ +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/module.h> #include <linux/of.h> @@ -263,7 +264,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) int ret = 0; int i; - guard(mutex)(&priv->mutex); + mutex_lock(&priv->mutex); domain->active++; @@ -303,6 +304,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) out: if (ret) domain->active--; + mutex_unlock(&priv->mutex); return ret; } @@ -868,10 +870,9 @@ static int j721e_soc_probe(struct platform_device *pdev) card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes); card->fully_routed = 1; - if (snd_soc_of_parse_card_name(card, "model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) + return ret; link_cnt = 0; conf_cnt = 0; diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c index 56aa4b22083b..05239ccc1f41 100644 --- a/sound/soc/ti/omap-abe-twl6040.c +++ b/sound/soc/ti/omap-abe-twl6040.c @@ -234,16 +234,13 @@ static int omap_abe_probe(struct platform_device *pdev) card->dapm_routes = audio_map; card->num_dapm_routes = ARRAY_SIZE(audio_map); - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } + ret = snd_soc_of_parse_card_name(card, "ti,model"); + if (ret) + return ret; ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); - if (ret) { - dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); + if (ret) return ret; - } dai_node = of_parse_phandle(node, "ti,mcpdm", 0); if (!dai_node) { @@ -299,8 +296,8 @@ static int omap_abe_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) - dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", - ret); + dev_err_probe(&pdev->dev, ret, + "devm_snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c index b795b9f66b0e..c8d791f05ae4 100644 --- a/sound/soc/ti/omap-dmic.c +++ b/sound/soc/ti/omap-dmic.c @@ -465,10 +465,9 @@ static int asoc_dmic_probe(struct platform_device *pdev) mutex_init(&dmic->mutex); dmic->fclk = devm_clk_get(dmic->dev, "fck"); - if (IS_ERR(dmic->fclk)) { - dev_err(dmic->dev, "can't get fck\n"); - return -ENODEV; - } + if (IS_ERR(dmic->fclk)) + return dev_err_probe(dmic->dev, PTR_ERR(dmic->fclk), + "can't get fck\n"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); if (!res) { diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c index e60f5b483fc5..c3034a8979dd 100644 --- a/sound/soc/ti/omap-hdmi.c +++ b/sound/soc/ti/omap-hdmi.c @@ -24,8 +24,6 @@ #define DRV_NAME "omap-hdmi-audio" struct hdmi_audio_data { - struct snd_soc_card *card; - const struct omap_hdmi_audio_ops *ops; struct device *dssdev; struct snd_dmaengine_dai_dma_data dma_data; @@ -375,12 +373,9 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) card->dev = dev; ret = devm_snd_soc_register_card(dev, card); - if (ret) { - dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); - ad->card = card; snd_soc_card_set_drvdata(card, ad); dev_set_drvdata(dev, ad); diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index 4d80f8a7a947..a3ab52b2b64e 100644 --- a/sound/soc/ti/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c @@ -17,7 +17,6 @@ */ #include <linux/platform_device.h> -#include <linux/platform_data/omap-twl4030.h> #include <linux/module.h> #include <linux/of.h> @@ -29,6 +28,7 @@ #include "omap-mcbsp.h" struct omap_twl4030 { + struct snd_soc_jack_gpio hs_jack_gpio; struct snd_soc_jack hs_jack; }; @@ -124,38 +124,18 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { }, }; -/* Headset jack detection gpios */ -static struct snd_soc_jack_gpio hs_jack_gpios[] = { - { - .name = "ti,jack-det", - .report = SND_JACK_HEADSET, - .debounce_time = 200, - }, -}; - -static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, - int connected, char *pin) -{ - if (!connected) - snd_soc_dapm_disable_pin(dapm, pin); -} - static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); - struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); - int ret = 0; + int ret; /* * This is a bit of a hack, but the GPIO is optional so we * only want to add the jack detection if the GPIO is there. */ if (of_property_present(card->dev->of_node, "ti,jack-det-gpio")) { - hs_jack_gpios[0].gpiod_dev = card->dev; - hs_jack_gpios[0].idx = 0; - ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", SND_JACK_HEADSET, &priv->hs_jack, hs_jack_pins, @@ -163,36 +143,19 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) if (ret) return ret; - ret = snd_soc_jack_add_gpios(&priv->hs_jack, - ARRAY_SIZE(hs_jack_gpios), - hs_jack_gpios); + priv->hs_jack_gpio.name = "ti,jack-det"; + priv->hs_jack_gpio.report = SND_JACK_HEADSET; + priv->hs_jack_gpio.debounce_time = 200; + priv->hs_jack_gpio.gpiod_dev = card->dev; + priv->hs_jack_gpio.idx = 0; + + ret = snd_soc_jack_add_gpios(&priv->hs_jack, 1, + &priv->hs_jack_gpio); if (ret) return ret; } - /* - * NULL pdata means we booted with DT. In this case the routing is - * provided and the card is fully routed, no need to mark pins. - */ - if (!pdata || !pdata->custom_routing) - return ret; - - /* Disable not connected paths if not used */ - twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); - twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); - twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); - twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); - twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); - - twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); - twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); - twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); - twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); - twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); - twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); - twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); - - return ret; + return 0; } /* Digital audio interface glue - connects codec <--> CPU */ @@ -237,11 +200,15 @@ static struct snd_soc_card omap_twl4030_card = { static int omap_twl4030_probe(struct platform_device *pdev) { - struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); - struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &omap_twl4030_card; + struct device_node *node, *dai_node; struct omap_twl4030 *priv; - int ret = 0; + struct property *prop; + int ret; + + node = pdev->dev.of_node; + if (!node) + return -ENODEV; card->dev = &pdev->dev; @@ -249,69 +216,52 @@ static int omap_twl4030_probe(struct platform_device *pdev) if (priv == NULL) return -ENOMEM; - if (node) { - struct device_node *dai_node; - struct property *prop; - - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - dai_node = of_parse_phandle(node, "ti,mcbsp", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McBSP node is not provided\n"); - return -EINVAL; - } - omap_twl4030_dai_links[0].cpus->dai_name = NULL; - omap_twl4030_dai_links[0].cpus->of_node = dai_node; - - omap_twl4030_dai_links[0].platforms->name = NULL; - omap_twl4030_dai_links[0].platforms->of_node = dai_node; - - dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); - if (!dai_node) { - card->num_links = 1; - } else { - omap_twl4030_dai_links[1].cpus->dai_name = NULL; - omap_twl4030_dai_links[1].cpus->of_node = dai_node; - - omap_twl4030_dai_links[1].platforms->name = NULL; - omap_twl4030_dai_links[1].platforms->of_node = dai_node; - } - - /* Optional: audio routing can be provided */ - prop = of_find_property(node, "ti,audio-routing", NULL); - if (prop) { - ret = snd_soc_of_parse_audio_routing(card, - "ti,audio-routing"); - if (ret) - return ret; - - card->fully_routed = 1; - } - } else if (pdata) { - if (pdata->card_name) { - card->name = pdata->card_name; - } else { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - if (!pdata->voice_connected) - card->num_links = 1; - } else { - dev_err(&pdev->dev, "Missing pdata\n"); + ret = snd_soc_of_parse_card_name(card, "ti,model"); + if (ret) + return ret; + + if (!card->name) { + dev_err(&pdev->dev, "Card name is not provided\n"); return -ENODEV; } + dai_node = of_parse_phandle(node, "ti,mcbsp", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McBSP node is not provided\n"); + return -EINVAL; + } + omap_twl4030_dai_links[0].cpus->dai_name = NULL; + omap_twl4030_dai_links[0].cpus->of_node = dai_node; + + omap_twl4030_dai_links[0].platforms->name = NULL; + omap_twl4030_dai_links[0].platforms->of_node = dai_node; + + dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); + if (!dai_node) { + card->num_links = 1; + } else { + omap_twl4030_dai_links[1].cpus->dai_name = NULL; + omap_twl4030_dai_links[1].cpus->of_node = dai_node; + + omap_twl4030_dai_links[1].platforms->name = NULL; + omap_twl4030_dai_links[1].platforms->of_node = dai_node; + } + + /* Optional: audio routing can be provided */ + prop = of_find_property(node, "ti,audio-routing", NULL); + if (prop) { + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); + if (ret) + return ret; + + card->fully_routed = 1; + } + snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "devm_snd_soc_register_card() failed\n"); return 0; } diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c index cfc23e0838c2..b4b26cb0cac5 100644 --- a/sound/soc/ti/rx51.c +++ b/sound/soc/ti/rx51.c @@ -358,21 +358,22 @@ static struct snd_soc_card rx51_sound_card = { static int rx51_soc_probe(struct platform_device *pdev) { struct rx51_audio_pdata *pdata; - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct snd_soc_card *card = &rx51_sound_card; int err; if (!of_machine_is_compatible("nokia,omap3-n900")) return -ENODEV; - card->dev = &pdev->dev; + card->dev = dev; if (np) { struct device_node *dai_node; dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0); if (!dai_node) { - dev_err(card->dev, "McBSP node is not provided\n"); + dev_err(dev, "McBSP node is not provided\n"); return -EINVAL; } rx51_dai[0].cpus->dai_name = NULL; @@ -382,7 +383,7 @@ static int rx51_soc_probe(struct platform_device *pdev) dai_node = of_parse_phandle(np, "nokia,audio-codec", 0); if (!dai_node) { - dev_err(card->dev, "Codec node is not provided\n"); + dev_err(dev, "Codec node is not provided\n"); return -EINVAL; } rx51_dai[0].codecs->name = NULL; @@ -390,7 +391,7 @@ static int rx51_soc_probe(struct platform_device *pdev) dai_node = of_parse_phandle(np, "nokia,audio-codec", 1); if (!dai_node) { - dev_err(card->dev, "Auxiliary Codec node is not provided\n"); + dev_err(dev, "Auxiliary Codec node is not provided\n"); return -EINVAL; } rx51_aux_dev[0].dlc.name = NULL; @@ -400,7 +401,7 @@ static int rx51_soc_probe(struct platform_device *pdev) dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0); if (!dai_node) { - dev_err(card->dev, "Headphone amplifier node is not provided\n"); + dev_err(dev, "Headphone amplifier node is not provided\n"); return -EINVAL; } rx51_aux_dev[1].dlc.name = NULL; @@ -409,40 +410,36 @@ static int rx51_soc_probe(struct platform_device *pdev) rx51_codec_conf[1].dlc.of_node = dai_node; } - pdata = devm_kzalloc(card->dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (pdata == NULL) return -ENOMEM; snd_soc_card_set_drvdata(card, pdata); - pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, + pdata->tvout_selection_gpio = devm_gpiod_get(dev, "tvout-selection", GPIOD_OUT_LOW); - if (IS_ERR(pdata->tvout_selection_gpio)) { - dev_err(card->dev, "could not get tvout selection gpio\n"); - return PTR_ERR(pdata->tvout_selection_gpio); - } + if (IS_ERR(pdata->tvout_selection_gpio)) + return dev_err_probe(dev, PTR_ERR(pdata->tvout_selection_gpio), + "could not get tvout selection gpio\n"); - pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", + pdata->eci_sw_gpio = devm_gpiod_get(dev, "eci-switch", GPIOD_OUT_HIGH); - if (IS_ERR(pdata->eci_sw_gpio)) { - dev_err(card->dev, "could not get eci switch gpio\n"); - return PTR_ERR(pdata->eci_sw_gpio); - } + if (IS_ERR(pdata->eci_sw_gpio)) + return dev_err_probe(dev, PTR_ERR(pdata->eci_sw_gpio), + "could not get eci switch gpio\n"); - pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, + pdata->speaker_amp_gpio = devm_gpiod_get(dev, "speaker-amplifier", GPIOD_OUT_LOW); - if (IS_ERR(pdata->speaker_amp_gpio)) { - dev_err(card->dev, "could not get speaker enable gpio\n"); - return PTR_ERR(pdata->speaker_amp_gpio); - } - - err = devm_snd_soc_register_card(card->dev, card); - if (err) { - dev_err(card->dev, "snd_soc_register_card failed (%d)\n", err); - return err; - } + if (IS_ERR(pdata->speaker_amp_gpio)) + return dev_err_probe(dev, PTR_ERR(pdata->speaker_amp_gpio), + "could not get speaker enable gpio\n"); + + err = devm_snd_soc_register_card(dev, card); + if (err) + return dev_err_probe(dev, err, + "snd_soc_register_card() failed\n"); return 0; } diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index d3dba21b2d04..78c82bf42a8f 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -795,16 +795,12 @@ int uniphier_aio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(dev, &uniphier_aio_component, chip->chip_spec->dais, chip->chip_spec->num_dais); - if (ret) { - dev_err(dev, "Register component failed.\n"); + if (ret) goto err_out_reset; - } ret = uniphier_aiodma_soc_register_platform(pdev); - if (ret) { - dev_err(dev, "Register platform failed.\n"); + if (ret) goto err_out_reset; - } return 0; diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h index d9fd61dd976f..1900ea01ce3d 100644 --- a/sound/soc/uniphier/aio.h +++ b/sound/soc/uniphier/aio.h @@ -156,7 +156,7 @@ struct uniphier_aio_selector { int hw; }; -/** +/* * 'SoftWare MAPping' setting of UniPhier AIO registers. * * We have to setup 'virtual' register maps to access 'real' registers of AIO. diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index ae6d326167d1..6d196b4b8802 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -68,10 +68,11 @@ static void mop500_of_node_put(void) of_node_put(mop500_dai_links[0].codecs->of_node); } -static int mop500_of_probe(struct platform_device *pdev, - struct device_node *np) +static int mop500_of_probe(struct snd_soc_card *card) { + struct device *dev = card->dev; struct device_node *codec_np, *msp_np[2]; + struct device_node *np = dev->of_node; int i; msp_np[0] = of_parse_phandle(np, "stericsson,cpu-dai", 0); @@ -79,7 +80,7 @@ static int mop500_of_probe(struct platform_device *pdev, codec_np = of_parse_phandle(np, "stericsson,audio-codec", 0); if (!(msp_np[0] && msp_np[1] && codec_np)) { - dev_err(&pdev->dev, "Phandle missing or invalid\n"); + dev_err(dev, "Phandle missing or invalid\n"); for (i = 0; i < 2; i++) of_node_put(msp_np[i]); of_node_put(codec_np); @@ -95,21 +96,20 @@ static int mop500_of_probe(struct platform_device *pdev, mop500_dai_links[i].codecs->name = NULL; } - snd_soc_of_parse_card_name(&mop500_card, "stericsson,card-name"); + snd_soc_of_parse_card_name(card, "stericsson,card-name"); return 0; } static int mop500_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; int ret; dev_dbg(&pdev->dev, "%s: Enter.\n", __func__); mop500_card.dev = &pdev->dev; - ret = mop500_of_probe(pdev, np); + ret = mop500_of_probe(&mop500_card); if (ret) return ret; diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 2a459267f0f9..feb683c55d11 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -433,5 +433,5 @@ void mop500_ab8500_remove(struct snd_soc_card *card) clk_put(drvdata->clk_ptr_ulpclk); clk_put(drvdata->clk_ptr_intclk); - snd_soc_card_set_drvdata(card, drvdata); + snd_soc_card_set_drvdata(card, NULL); } diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index f15336197ed3..95312b4ddc5b 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -9,8 +9,6 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> #include <linux/sizes.h> #include <sound/asoundef.h> @@ -384,7 +382,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, if (err) { dev_err(component->dev, "Unable to set constraint on period bytes\n"); - return err; + goto error; } /* Resize the buffer bytes as divisible by 64 */ @@ -394,7 +392,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, if (err) { dev_err(component->dev, "Unable to set constraint on buffer bytes\n"); - return err; + goto error; } /* Set periods as integer multiple */ @@ -403,7 +401,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, if (err < 0) { dev_err(component->dev, "Unable to set constraint on periods to be integer\n"); - return err; + goto error; } /* enable DMA IOC irq */ @@ -412,6 +410,14 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, writel(val, stream_data->mmio + XLNX_AUD_CTRL); return 0; + +error: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + adata->play_stream = NULL; + else + adata->capture_stream = NULL; + kfree(stream_data); + return err; } static int xlnx_formatter_pcm_close(struct snd_soc_component *component, diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c index ca915a001ad5..273258125497 100644 --- a/sound/soc/xilinx/xlnx_i2s.c +++ b/sound/soc/xilinx/xlnx_i2s.c @@ -9,9 +9,8 @@ #include <linux/io.h> #include <linux/module.h> -#include <linux/of.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -174,7 +173,6 @@ static int xlnx_i2s_probe(struct platform_device *pdev) int ret; u32 format; struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) @@ -184,18 +182,16 @@ static int xlnx_i2s_probe(struct platform_device *pdev) if (IS_ERR(drv_data->base)) return PTR_ERR(drv_data->base); - ret = of_property_read_u32(node, "xlnx,num-channels", &drv_data->channels); - if (ret < 0) { - dev_err(dev, "cannot get supported channels\n"); - return ret; - } + ret = device_property_read_u32(dev, "xlnx,num-channels", &drv_data->channels); + if (ret < 0) + return dev_err_probe(dev, ret, "cannot get supported channels\n"); + drv_data->channels *= 2; - ret = of_property_read_u32(node, "xlnx,dwidth", &drv_data->data_width); - if (ret < 0) { - dev_err(dev, "cannot get data width\n"); - return ret; - } + ret = device_property_read_u32(dev, "xlnx,dwidth", &drv_data->data_width); + if (ret < 0) + return dev_err_probe(dev, ret, "cannot get data width\n"); + switch (drv_data->data_width) { case 16: format = SNDRV_PCM_FMTBIT_S16_LE; @@ -207,7 +203,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) return -EINVAL; } - if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) { + if (device_is_compatible(dev, "xlnx,i2s-transmitter-1.0")) { drv_data->dai_drv.name = "xlnx_i2s_playback"; drv_data->dai_drv.playback.stream_name = "Playback"; drv_data->dai_drv.playback.formats = format; @@ -215,7 +211,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) drv_data->dai_drv.playback.channels_max = drv_data->channels; drv_data->dai_drv.playback.rates = SNDRV_PCM_RATE_8000_192000; drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; - } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { + } else if (device_is_compatible(dev, "xlnx,i2s-receiver-1.0")) { drv_data->dai_drv.name = "xlnx_i2s_capture"; drv_data->dai_drv.capture.stream_name = "Capture"; drv_data->dai_drv.capture.formats = format; @@ -233,10 +229,8 @@ static int xlnx_i2s_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component, &drv_data->dai_drv, 1); - if (ret) { - dev_err(&pdev->dev, "i2s component registration failed\n"); + if (ret) return ret; - } dev_info(&pdev->dev, "%s DAI registered\n", drv_data->dai_drv.name); diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c index 017a64ab9f1e..a70ca6beb646 100644 --- a/sound/soc/xilinx/xlnx_spdif.c +++ b/sound/soc/xilinx/xlnx_spdif.c @@ -10,8 +10,7 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of.h> -#include <linux/of_platform.h> +#include <linux/property.h> #include <linux/platform_device.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -242,28 +241,24 @@ static int xlnx_spdif_probe(struct platform_device *pdev) struct spdif_dev_data *ctx; struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; ctx->axi_clk = devm_clk_get_enabled(dev, "s_axi_aclk"); - if (IS_ERR(ctx->axi_clk)) { - ret = PTR_ERR(ctx->axi_clk); - dev_err(dev, "failed to get s_axi_aclk(%d)\n", ret); - return ret; - } + if (IS_ERR(ctx->axi_clk)) + return dev_err_probe(dev, PTR_ERR(ctx->axi_clk), + "failed to get s_axi_aclk\n"); ctx->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ctx->base)) return PTR_ERR(ctx->base); - ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode); - if (ret < 0) { - dev_err(dev, "cannot get SPDIF mode\n"); - return ret; - } + ret = device_property_read_u32(dev, "xlnx,spdif-mode", &ctx->mode); + if (ret < 0) + return dev_err_probe(dev, ret, "cannot get SPDIF mode\n"); + if (ctx->mode) { dai_drv = &xlnx_spdif_tx_dai; } else { @@ -274,29 +269,23 @@ static int xlnx_spdif_probe(struct platform_device *pdev) ret = devm_request_irq(dev, ret, xlnx_spdifrx_irq_handler, 0, "XLNX_SPDIF_RX", ctx); - if (ret) { - dev_err(dev, "spdif rx irq request failed\n"); - return -ENODEV; - } + if (ret) + return ret; init_waitqueue_head(&ctx->chsts_q); dai_drv = &xlnx_spdif_rx_dai; } - ret = of_property_read_u32(node, "xlnx,aud_clk_i", &ctx->aclk); - if (ret < 0) { - dev_err(dev, "cannot get aud_clk_i value\n"); - return ret; - } + ret = device_property_read_u32(dev, "xlnx,aud_clk_i", &ctx->aclk); + if (ret < 0) + return dev_err_probe(dev, ret, "cannot get aud_clk_i value\n"); dev_set_drvdata(dev, ctx); ret = devm_snd_soc_register_component(dev, &xlnx_spdif_component, dai_drv, 1); - if (ret) { - dev_err(dev, "SPDIF component registration failed\n"); + if (ret) return ret; - } writel(XSPDIF_SOFT_RESET_VALUE, ctx->base + XSPDIF_SOFT_RESET_REG); dev_info(dev, "%s DAI registered\n", dai_drv->name); diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c index 9ad86c54e3ea..fd0990201b40 100644 --- a/sound/soc/xtensa/xtfpga-i2s.c +++ b/sound/soc/xtensa/xtfpga-i2s.c @@ -533,34 +533,25 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) int err, irq; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); - if (!i2s) { - err = -ENOMEM; - goto err; - } + if (!i2s) + return -ENOMEM; + platform_set_drvdata(pdev, i2s); i2s->dev = &pdev->dev; dev_dbg(&pdev->dev, "dev: %p, i2s: %p\n", &pdev->dev, i2s); i2s->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(i2s->regs)) { - err = PTR_ERR(i2s->regs); - goto err; - } + if (IS_ERR(i2s->regs)) + return PTR_ERR(i2s->regs); i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs, &xtfpga_i2s_regmap_config); - if (IS_ERR(i2s->regmap)) { - dev_err(&pdev->dev, "regmap init failed\n"); - err = PTR_ERR(i2s->regmap); - goto err; - } + if (IS_ERR(i2s->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), "regmap init failed\n"); i2s->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(i2s->clk)) { - dev_err(&pdev->dev, "couldn't get clock\n"); - err = PTR_ERR(i2s->clk); - goto err; - } + if (IS_ERR(i2s->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->clk), "couldn't get clock\n"); regmap_write(i2s->regmap, XTFPGA_I2S_CONFIG, (0x1 << XTFPGA_I2S_CONFIG_CHANNEL_BASE)); @@ -568,41 +559,34 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, XTFPGA_I2S_INT_UNDERRUN); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - err = irq; - goto err; - } + if (irq < 0) + return irq; + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, xtfpga_i2s_threaded_irq_handler, IRQF_SHARED | IRQF_ONESHOT, pdev->name, i2s); - if (err < 0) { - dev_err(&pdev->dev, "request_irq failed\n"); - goto err; - } + if (err < 0) + return err; err = devm_snd_soc_register_component(&pdev->dev, &xtfpga_i2s_component, xtfpga_i2s_dai, ARRAY_SIZE(xtfpga_i2s_dai)); - if (err < 0) { - dev_err(&pdev->dev, "couldn't register component\n"); - goto err; - } + if (err < 0) + return err; + pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { err = xtfpga_i2s_runtime_resume(&pdev->dev); - if (err) - goto err_pm_disable; + if (err) { + pm_runtime_disable(&pdev->dev); + return err; + } } - return 0; -err_pm_disable: - pm_runtime_disable(&pdev->dev); -err: - dev_err(&pdev->dev, "%s: err = %d\n", __func__, err); - return err; + return 0; } static void xtfpga_i2s_remove(struct platform_device *pdev) |
