diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 09:20:49 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 09:20:49 -0700 |
| commit | a8e7ef3cec99ba2487110e01d77a8a278593b3e9 (patch) | |
| tree | 569c8df48d1cab987ab7daa95ba7a34063c5d95f | |
| parent | 4a57e0913e8c7fff407e97909f4ae48caa84d612 (diff) | |
| parent | e0da8a8cac74f4b9f577979d131f0d2b88a84487 (diff) | |
| download | lwn-a8e7ef3cec99ba2487110e01d77a8a278593b3e9.tar.gz lwn-a8e7ef3cec99ba2487110e01d77a8a278593b3e9.zip | |
Merge tag 'sound-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"Nothing too thrilling here, but we see lots of driver updates and bug
fixes, including quirk additions and refactoring works, while there
have been little changes in the core functionality. Here are some
highlights:
Core:
- Add validation for the control API put callback
- Fixes in compress-offload API timestamp handling
- Continued ASoC core API cleanups
ASoC:
- Add support for bus keepers (for Apple devices in future)
- Enhancements to the SDCA support, including retaskable jacks
- Test improvements for Cirrus Logic drivers
- Lots of fixes for the NXP, nVidia and Qualcomm
- Support for AMD RPL DMIC, Cirrus Logic CS42L43 and CS47L47, nVidia
machines with CPCAP and WM8962
USB-audio:
- Quirks for Huawei Headset, Focusrite Novation, MV-Silicon, Studio
1824, Arturia AF16Rig, Hotone Audio, Feaulle Rainbow, PreSonus
AudioBox, Moondrop Ju Jiu, Scarlett 18i20, etc
- Extended mixer volume quirk handling
- UAF and other fixes for us144mkii, 6fire and caiaq drivers
HD-audio:
- Add quirks or fixes for Acer, Lenovo, HP, ASUS machines
- Fixes & cleanups of GPIO helper code
Misc:
- Add suspend/resume support for multiple legacy ISA and Apple
drivers
- Further regression fixes for ctxfi driver"
* tag 'sound-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (359 commits)
ALSA: control: Validate buf_len before strnlen() in snd_ctl_elem_init_enum_names()
ALSA: usb-audio: Fix missing error handling for get_min_max*()
ALSA: hda/realtek - fixed speaker no sound update
ALSA: hda/realtek: Add quirk for Acer PT316-51S headset mic
ALSA: usb-audio: Exclude Scarlett 18i20 1st Gen from SKIP_IFACE_SETUP
ALSA: hda/realtek: Add quirk for Legion S7 15IMH
ALSA: hda/realtek: Add quirk for HP Spectre x360 14-ea
ALSA: caiaq: take a reference on the USB device in create_card()
ASoC: dt-bindings: rockchip: convert rk3399-gru-sound to DT Schema
ALSA: sscape: Add suspend and resume support
ALSA: sscape: Cache per-card resources for board reinitialization
ALSA: usb-audio: Do not expose sticky mixers
ALSA: usb-audio: Move volume control resolution check into a function
ALSA: usb-audio: Add error checks against get_min_max*()
ALSA: usb-audio: Add quirk for PreSonus AudioBox USB
ALSA: interwave: guard PM-only restore helpers with CONFIG_PM
ALSA: usb-audio: Evaluate packsize caps at the right place
ALSA: sc6000: Restore board setup across suspend
ALSA: sc6000: Keep the programmed board state in card-private data
ALSA: 6fire: fix use-after-free on disconnect
...
376 files changed, 9033 insertions, 3316 deletions
diff --git a/Documentation/devicetree/bindings/sound/adi,ssm2305.txt b/Documentation/devicetree/bindings/sound/adi,ssm2305.txt deleted file mode 100644 index a9c9d83c8a30..000000000000 --- a/Documentation/devicetree/bindings/sound/adi,ssm2305.txt +++ /dev/null @@ -1,14 +0,0 @@ -Analog Devices SSM2305 Speaker Amplifier -======================================== - -Required properties: - - compatible : "adi,ssm2305" - - shutdown-gpios : The gpio connected to the shutdown pin. - The gpio signal is ACTIVE_LOW. - -Example: - -ssm2305: analog-amplifier { - compatible = "adi,ssm2305"; - shutdown-gpios = <&gpio3 20 GPIO_ACTIVE_LOW>; -}; diff --git a/Documentation/devicetree/bindings/sound/adi,ssm2305.yaml b/Documentation/devicetree/bindings/sound/adi,ssm2305.yaml new file mode 100644 index 000000000000..b841da2dc284 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/adi,ssm2305.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/adi,ssm2305.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices SSM2305 Class-D Speaker Amplifier + +maintainers: + - Lars-Peter Clausen <lars@metafoo.de> + +description: + The SSM2305 is a filterless, high efficiency, mono 2.8 W Class-D + audio amplifier with a micropower shutdown mode controlled via a + dedicated active-low GPIO pin. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: adi,ssm2305 + + shutdown-gpios: + maxItems: 1 + description: + GPIO connected to the shutdown pin (SD) of the SSM2305. + The pin is active-low; asserting it puts the device into + micropower shutdown mode. + +required: + - compatible + - shutdown-gpios + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + analog-amplifier { + compatible = "adi,ssm2305"; + shutdown-gpios = <&gpio3 20 GPIO_ACTIVE_LOW>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml index 994d68c074a9..b9abb10942ba 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -35,6 +35,10 @@ properties: dvdd-supply: true + firmware-name: + maxItems: 1 + description: Name of the *_acf.bin file used for amplifier initialization + awinic,audio-channel: description: It is used to distinguish multiple PA devices, so that different diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml index 99a536601cc7..376928d1f64b 100644 --- a/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml @@ -16,6 +16,8 @@ description: | DAC for headphone output, two integrated Class D amplifiers for loudspeakers, and two ADCs for wired headset microphone input or stereo line input. PDM inputs are provided for digital microphones. + CS42L43B variant adds dedicated PDM interface, SoundWire Clock Gearing + support and more decimators to ISRCs. allOf: - $ref: dai-common.yaml# @@ -24,6 +26,7 @@ properties: compatible: enum: - cirrus,cs42l43 + - cirrus,cs42l43b reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt deleted file mode 100644 index 7a296784eb37..000000000000 --- a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt +++ /dev/null @@ -1,42 +0,0 @@ -* Hisilicon 6210 i2s controller - -Required properties: - -- compatible: should be one of the following: - - "hisilicon,hi6210-i2s" -- reg: physical base address of the i2s controller unit and length of - memory mapped region. -- interrupts: should contain the i2s interrupt. -- clocks: a list of phandle + clock-specifier pairs, one for each entry - in clock-names. -- clock-names: should contain following: - - "dacodec" - - "i2s-base" -- dmas: DMA specifiers for tx dma. See the DMA client binding, - Documentation/devicetree/bindings/dma/dma.txt -- dma-names: should be "tx" and "rx" -- hisilicon,sysctrl-syscon: phandle to sysctrl syscon -- #sound-dai-cells: Should be set to 1 (for multi-dai) - - The dai cell indexes reference the following interfaces: - 0: S2 interface - (Currently that is the only one available, but more may be - supported in the future) - -Example for the hi6210 i2s controller: - -i2s0: i2s@f7118000{ - compatible = "hisilicon,hi6210-i2s"; - reg = <0x0 0xf7118000 0x0 0x8000>; /* i2s unit */ - interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>; /* 155 "DigACodec_intr"-32 */ - clocks = <&sys_ctrl HI6220_DACODEC_PCLK>, - <&sys_ctrl HI6220_BBPPLL0_DIV>; - clock-names = "dacodec", "i2s-base"; - dmas = <&dma0 15 &dma0 14>; - dma-names = "rx", "tx"; - hisilicon,sysctrl-syscon = <&sys_ctrl>; - #sound-dai-cells = <1>; -}; - -Then when referencing the i2s controller: - sound-dai = <&i2s0 0>; /* index 0 => S2 interface */ - diff --git a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml new file mode 100644 index 000000000000..5171f984630b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/hisilicon,hi6210-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HiSilicon hi6210 I2S controller + +maintainers: + - John Stultz <john.stultz@linaro.org> + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: hisilicon,hi6210-i2s + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: dacodec + - const: i2s-base + + dmas: + maxItems: 2 + + dma-names: + items: + - const: tx + - const: rx + + hisilicon,sysctrl-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to sysctrl syscon + + "#sound-dai-cells": + const: 1 + description: | + The dai cell indexes reference the following interfaces: + 0: S2 interface + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - dmas + - dma-names + - hisilicon,sysctrl-syscon + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/hi6220-clock.h> + + i2s@f7118000 { + compatible = "hisilicon,hi6210-i2s"; + reg = <0xf7118000 0x8000>; + interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&sys_ctrl HI6220_DACODEC_PCLK>, + <&sys_ctrl HI6220_BBPPLL0_DIV>; + clock-names = "dacodec", "i2s-base"; + dmas = <&dma0 14>, <&dma0 15>; + dma-names = "tx", "rx"; + hisilicon,sysctrl-syscon = <&sys_ctrl>; + #sound-dai-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml index 3c75c8c78987..5424d4f16f52 100644 --- a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml @@ -24,6 +24,7 @@ patternProperties: cpu/codec dais. type: object + $ref: tdm-slot.yaml# properties: link-name: @@ -38,13 +39,9 @@ patternProperties: - i2s - dsp_b - dai-tdm-slot-num: - description: see tdm-slot.txt. - $ref: /schemas/types.yaml#/definitions/uint32 + dai-tdm-slot-num: true - dai-tdm-slot-width: - description: see tdm-slot.txt. - $ref: /schemas/types.yaml#/definitions/uint32 + dai-tdm-slot-width: true playback-only: description: link is used only for playback diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt2701-wm8960.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt2701-wm8960.yaml index cf985461a995..bb6a405b263e 100644 --- a/Documentation/devicetree/bindings/sound/mediatek,mt2701-wm8960.yaml +++ b/Documentation/devicetree/bindings/sound/mediatek,mt2701-wm8960.yaml @@ -28,8 +28,6 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle description: The phandle of the WM8960 audio codec. -unevaluatedProperties: false - required: - compatible - mediatek,platform @@ -38,6 +36,8 @@ required: - pinctrl-names - pinctrl-0 +additionalProperties: false + examples: - | sound { diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt7986-wm8960.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt7986-wm8960.yaml index 09247ceea3f7..f21cad4bae15 100644 --- a/Documentation/devicetree/bindings/sound/mediatek,mt7986-wm8960.yaml +++ b/Documentation/devicetree/bindings/sound/mediatek,mt7986-wm8960.yaml @@ -36,14 +36,14 @@ properties: required: - sound-dai -unevaluatedProperties: false - required: - compatible - audio-routing - platform - codec +unevaluatedProperties: false + examples: - | sound { diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8173-rt5650-rt5514.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8173-rt5650-rt5514.yaml new file mode 100644 index 000000000000..ed698c9ff42b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8173-rt5650-rt5514.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mt8173-rt5650-rt5514.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek MT8173 with RT5650 and RT5514 audio codecs + +maintainers: + - Koro Chen <koro.chen@mediatek.com> + +properties: + compatible: + const: mediatek,mt8173-rt5650-rt5514 + + mediatek,audio-codec: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: Phandles of rt5650 and rt5514 codecs + items: + - description: phandle of rt5650 codec + - description: phandle of rt5514 codec + + mediatek,platform: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of MT8173 ASoC platform. + +required: + - compatible + - mediatek,audio-codec + - mediatek,platform + +additionalProperties: false + +examples: + - | + sound { + compatible = "mediatek,mt8173-rt5650-rt5514"; + mediatek,audio-codec = <&rt5650>, <&rt5514>; + mediatek,platform = <&afe>; + }; +... diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml index 7ba2ea2dfa0b..539de75eb20d 100644 --- a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml @@ -105,12 +105,12 @@ patternProperties: required: - link-name -unevaluatedProperties: false - required: - compatible - mediatek,platform +unevaluatedProperties: false + examples: - | sound { diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt deleted file mode 100644 index e8b3c80c6fff..000000000000 --- a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt +++ /dev/null @@ -1,15 +0,0 @@ -MT8173 with RT5650 RT5514 CODECS - -Required properties: -- compatible : "mediatek,mt8173-rt5650-rt5514" -- mediatek,audio-codec: the phandles of rt5650 and rt5514 codecs -- mediatek,platform: the phandle of MT8173 ASoC platform - -Example: - - sound { - compatible = "mediatek,mt8173-rt5650-rt5514"; - mediatek,audio-codec = <&rt5650 &rt5514>; - mediatek,platform = <&afe>; - }; - diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-cpcap.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-cpcap.yaml new file mode 100644 index 000000000000..69af2022d0fa --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-cpcap.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-cpcap.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with CPCAP CODEC + +maintainers: + - Svyatoslav Ryhel <clamor95@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^motorola,tegra-audio-cpcap(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-cpcap + + nvidia,audio-routing: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: + A list of the connections between audio components. Each entry is a + pair of strings, the first being the connection's sink, the second + being the connection's source. Valid names for sources and sinks are + the pins (documented in the binding document), and the jacks on the + board. + minItems: 2 + items: + enum: + # Board Connectors + - Speakers + - Int Spk + - Earpiece + - Int Mic + - Headset Mic + - Internal Mic 1 + - Internal Mic 2 + - Headphone + - Headphones + - Headphone Jack + - Mic Jack + + # CODEC Pins + - MICR + - HSMIC + - EMUMIC + - MICL + - EXTR + - EXTL + - EP + - SPKR + - SPKL + - LINER + - LINEL + - HSR + - HSL + - EMUR + - EMUL + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/tegra20-car.h> + #include <dt-bindings/soc/tegra-pmc.h> + sound { + compatible = "motorola,tegra-audio-cpcap-olympus", + "nvidia,tegra-audio-cpcap"; + nvidia,model = "Motorola Atrix 4G (MB860) CPCAP"; + + nvidia,audio-routing = + "Headphones", "HSR", + "Headphones", "HSL", + "Int Spk", "SPKR", + "Int Spk", "SPKL", + "Earpiece", "EP", + "HSMIC", "Mic Jack", + "MICR", "Internal Mic 1", + "MICL", "Internal Mic 2"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&cpcap_audio>; + + clocks = <&tegra_car TEGRA20_CLK_PLL_A>, + <&tegra_car TEGRA20_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA20_CLK_CDEV1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max9808x.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max9808x.yaml index 241d20f3aad0..4957645a8e03 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max9808x.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max9808x.yaml @@ -35,10 +35,15 @@ properties: items: enum: # Board Connectors + - Speakers - Int Spk + - Headphone + - Headphones - Headphone Jack - Earpiece - Headset Mic + - Mic Jack + - Int Mic - Internal Mic 1 - Internal Mic 2 diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8962.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8962.yaml new file mode 100644 index 000000000000..2c3bf5a02a34 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8962.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-wm8962.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with WM8962 CODEC + +maintainers: + - Svyatoslav Ryhel <clamor95@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-wm8962(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-wm8962 + + nvidia,audio-routing: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: + A list of the connections between audio components. Each entry is a + pair of strings, the first being the connection's sink, the second + being the connection's source. Valid names for sources and sinks are + the pins (documented in the binding document), and the jacks on the + board. + minItems: 2 + items: + enum: + # Board Connectors + - Speakers + - Int Spk + - Earpiece + - Int Mic + - Headset Mic + - Internal Mic 1 + - Internal Mic 2 + - Headphone + - Headphones + - Headphone Jack + - Mic Jack + + # CODEC Pins + - IN1L + - IN1R + - IN2L + - IN2R + - IN3L + - IN3R + - IN4L + - IN4R + - DMICDAT + - HPOUTL + - HPOUTR + - SPKOUT + - SPKOUTL + - SPKOUTR + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/tegra30-car.h> + #include <dt-bindings/soc/tegra-pmc.h> + sound { + compatible = "microsoft,tegra-audio-wm8962-surface-rt", + "nvidia,tegra-audio-wm8962"; + nvidia,model = "Microsoft Surface RT WM8962"; + + nvidia,audio-routing = + "Headphone Jack", "HPOUTR", + "Headphone Jack", "HPOUTL", + "Int Spk", "SPKOUTR", + "Int Spk", "SPKOUTL"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&wm8962>; + + clocks = <&tegra_car TEGRA30_CLK_PLL_A>, + <&tegra_car TEGRA30_CLK_PLL_A_OUT0>, + <&tegra_pmc TEGRA_PMC_CLK_OUT_1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml index 08c618e7e428..2b27d6c8f58f 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml @@ -126,13 +126,16 @@ patternProperties: reg: contains: # MI2S DAI ID range PRIMARY_MI2S_RX - QUATERNARY_MI2S_TX and - # QUINARY_MI2S_RX - QUINARY_MI2S_TX + # QUINARY_MI2S_RX - QUINARY_MI2S_TX and + # LPI_MI2S_RX_0 - SENARY_MI2S_TX items: oneOf: - minimum: 16 maximum: 23 - minimum: 127 maximum: 128 + - minimum: 137 + maximum: 148 then: required: - qcom,sd-lines diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.txt deleted file mode 100644 index 72d3cf4c2606..000000000000 --- a/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.txt +++ /dev/null @@ -1,22 +0,0 @@ -ROCKCHIP with MAX98357A/RT5514/DA7219 codecs on GRU boards - -Required properties: -- compatible: "rockchip,rk3399-gru-sound" -- rockchip,cpu: The phandle of the Rockchip I2S controller that's - connected to the codecs -- rockchip,codec: The phandle of the audio codecs - -Optional properties: -- dmic-wakeup-delay-ms : specify delay time (ms) for DMIC ready. - If this option is specified, which means it's required dmic need - delay for DMIC to ready so that rt5514 can avoid recording before - DMIC send valid data - -Example: - -sound { - compatible = "rockchip,rk3399-gru-sound"; - rockchip,cpu = <&i2s0>; - rockchip,codec = <&max98357a &rt5514 &da7219>; - dmic-wakeup-delay-ms = <20>; -}; diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.yaml b/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.yaml new file mode 100644 index 000000000000..e9d13695cc77 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/rockchip,rk3399-gru-sound.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip with MAX98357A/RT5514/DA7219 codecs on GRU boards + +maintainers: + - Heiko Stuebner <heiko@sntech.de> + +properties: + compatible: + const: rockchip,rk3399-gru-sound + + rockchip,cpu: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + List of phandles to the Rockchip CPU DAI controllers connected to codecs + minItems: 1 + items: + - items: + - description: Phandle to the Rockchip I2S controllers + - items: + - description: | + Phandle to the Rockchip SPDIF controller. Required when a + DisplayPort audio codec is referenced in rockchip,codec + + rockchip,codec: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + The phandles of the audio codecs connected to the Rockchip CPU DAI + controllers + minItems: 1 + maxItems: 6 + items: + maxItems: 1 + + dmic-wakeup-delay-ms: + description: | + specify delay time (ms) for DMIC ready. + If this option is specified, a delay is required for DMIC to get ready + so that rt5514 can avoid recording before DMIC sends valid data + +required: + - compatible + - rockchip,cpu + - rockchip,codec + +additionalProperties: false + +examples: + - | + sound { + compatible = "rockchip,rk3399-gru-sound"; + rockchip,cpu = <&i2s0 &spdif>; + rockchip,codec = <&max98357a &rt5514 &da7219 &cdn_dp>; + dmic-wakeup-delay-ms = <20>; + }; + diff --git a/Documentation/devicetree/bindings/sound/rockchip,rockchip-audio-max98090.yaml b/Documentation/devicetree/bindings/sound/rockchip,rockchip-audio-max98090.yaml new file mode 100644 index 000000000000..5351d5f02edf --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,rockchip-audio-max98090.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/rockchip,rockchip-audio-max98090.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip audio complex with MAX98090 codec + +maintainers: + - Fabio Estevam <festevam@gmail.com> + +properties: + compatible: + const: rockchip,rockchip-audio-max98090 + + rockchip,model: + $ref: /schemas/types.yaml#/definitions/string + description: The user-visible name of this sound complex. + + rockchip,i2s-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the Rockchip I2S controller. + + rockchip,audio-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the MAX98090 audio codec. + + rockchip,headset-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the external chip for jack detection. + + rockchip,hdmi-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the HDMI device for HDMI codec. + +required: + - compatible + - rockchip,model + - rockchip,i2s-controller + +allOf: + - if: + required: + - rockchip,audio-codec + then: + required: + - rockchip,headset-codec + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "rockchip,rockchip-audio-max98090"; + rockchip,model = "ROCKCHIP-I2S"; + rockchip,i2s-controller = <&i2s>; + rockchip,audio-codec = <&max98090>; + rockchip,headset-codec = <&headsetcodec>; + }; diff --git a/Documentation/devicetree/bindings/sound/rockchip-max98090.txt b/Documentation/devicetree/bindings/sound/rockchip-max98090.txt deleted file mode 100644 index e9c58b204399..000000000000 --- a/Documentation/devicetree/bindings/sound/rockchip-max98090.txt +++ /dev/null @@ -1,42 +0,0 @@ -ROCKCHIP with MAX98090 CODEC - -Required properties: -- compatible: "rockchip,rockchip-audio-max98090" -- rockchip,model: The user-visible name of this sound complex -- rockchip,i2s-controller: The phandle of the Rockchip I2S controller that's - connected to the CODEC - -Optional properties: -- rockchip,audio-codec: The phandle of the MAX98090 audio codec. -- rockchip,headset-codec: The phandle of Ext chip for jack detection. This is - required if there is rockchip,audio-codec. -- rockchip,hdmi-codec: The phandle of HDMI device for HDMI codec. - -Example: - -/* For max98090-only board. */ -sound { - compatible = "rockchip,rockchip-audio-max98090"; - rockchip,model = "ROCKCHIP-I2S"; - rockchip,i2s-controller = <&i2s>; - rockchip,audio-codec = <&max98090>; - rockchip,headset-codec = <&headsetcodec>; -}; - -/* For HDMI-only board. */ -sound { - compatible = "rockchip,rockchip-audio-max98090"; - rockchip,model = "ROCKCHIP-I2S"; - rockchip,i2s-controller = <&i2s>; - rockchip,hdmi-codec = <&hdmi>; -}; - -/* For max98090 plus HDMI board. */ -sound { - compatible = "rockchip,rockchip-audio-max98090"; - rockchip,model = "ROCKCHIP-I2S"; - rockchip,i2s-controller = <&i2s>; - rockchip,audio-codec = <&max98090>; - rockchip,headset-codec = <&headsetcodec>; - rockchip,hdmi-codec = <&hdmi>; -}; diff --git a/Documentation/devicetree/bindings/sound/simple-card.yaml b/Documentation/devicetree/bindings/sound/simple-card.yaml index 533d0a1da56e..a14716b2732f 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.yaml +++ b/Documentation/devicetree/bindings/sound/simple-card.yaml @@ -27,14 +27,6 @@ definitions: description: dai-link uses bit clock inversion $ref: /schemas/types.yaml#/definitions/flag - dai-tdm-slot-num: - description: see tdm-slot.txt. - $ref: /schemas/types.yaml#/definitions/uint32 - - dai-tdm-slot-width: - description: see tdm-slot.txt. - $ref: /schemas/types.yaml#/definitions/uint32 - system-clock-frequency: description: | If a clock is specified and a multiplication factor is given with @@ -115,6 +107,8 @@ definitions: dai: type: object + $ref: tdm-slot.yaml# + properties: sound-dai: maxItems: 1 @@ -133,10 +127,6 @@ definitions: bitclock-master: $ref: /schemas/types.yaml#/definitions/flag - dai-tdm-slot-num: - $ref: "#/definitions/dai-tdm-slot-num" - dai-tdm-slot-width: - $ref: "#/definitions/dai-tdm-slot-width" clocks: maxItems: 1 system-clock-frequency: diff --git a/Documentation/devicetree/bindings/sound/tdm-slot.txt b/Documentation/devicetree/bindings/sound/tdm-slot.txt deleted file mode 100644 index 4bb513ae62fc..000000000000 --- a/Documentation/devicetree/bindings/sound/tdm-slot.txt +++ /dev/null @@ -1,29 +0,0 @@ -TDM slot: - -This specifies audio DAI's TDM slot. - -TDM slot properties: -dai-tdm-slot-num : Number of slots in use. -dai-tdm-slot-width : Width in bits for each slot. -dai-tdm-slot-tx-mask : Transmit direction slot mask, optional -dai-tdm-slot-rx-mask : Receive direction slot mask, optional - -For instance: - dai-tdm-slot-num = <2>; - dai-tdm-slot-width = <8>; - dai-tdm-slot-tx-mask = <0 1>; - dai-tdm-slot-rx-mask = <1 0>; - -And for each specified driver, there could be one .of_xlate_tdm_slot_mask() -to specify an explicit mapping of the channels and the slots. If it's absent -the default snd_soc_of_xlate_tdm_slot_mask() will be used to generating the -tx and rx masks. - -For snd_soc_of_xlate_tdm_slot_mask(), the tx and rx masks will use a 1 bit -for an active slot as default, and the default active bits are at the LSB of -the masks. - -The explicit masks are given as array of integers, where the first -number presents bit-0 (LSB), second presents bit-1, etc. Any non zero -number is considered 1 and 0 is 0. snd_soc_of_xlate_tdm_slot_mask() -does not do anything, if either mask is set non zero value. diff --git a/Documentation/devicetree/bindings/sound/tdm-slot.yaml b/Documentation/devicetree/bindings/sound/tdm-slot.yaml new file mode 100644 index 000000000000..457a899e8872 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tdm-slot.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/tdm-slot.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Time Division Multiplexing (TDM) Slot Parameters + +maintainers: + - Liam Girdwood <lgirdwood@gmail.com> + +select: false + +properties: + 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 + + dai-tdm-idle-mode: + $ref: /schemas/types.yaml#/definitions/string + enum: + - none + - off + - zero + - pulldown + - hiz + - pullup + - drivehigh + description: Drive mode for inactive/idle TDM slots. For hardware that + implements .set_tdm_idle(). Optional. "None" represents undefined + behaviour and is the same as not setting this property. + +patternProperties: + '^dai-tdm-slot-[rt]x-mask$': + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Slot mask for active TDM slots. Optional. Drivers may + specify .xlate_tdm_slot_mask() to generate a slot mask dynamically. If + neither this property nor a driver-specific function are specified, the + default snd_soc_xlate_tdm_slot_mask() function will be used to generate + a mask. The first element of the array is slot 0 (LSB). Any nonzero + value will be treated as 1. + + '^dai-tdm-slot-[rt]x-idle-mask$': + $ref: /schemas/types.yaml#/definitions/uint32 + description: Idle slot mask. Optional. A bit being set to 1 indicates + that the corresponding TDM slot is inactive/idle. + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/sound/ti,tas2770.yaml b/Documentation/devicetree/bindings/sound/ti,tas2770.yaml index 8eab98a0f7a2..8d49fbcf0b9b 100644 --- a/Documentation/devicetree/bindings/sound/ti,tas2770.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas2770.yaml @@ -30,7 +30,7 @@ properties: description: | I2C address of the device can be between 0x41 to 0x48. - reset-gpio: + reset-gpios: maxItems: 1 description: GPIO used to reset the device. @@ -82,7 +82,7 @@ examples: #sound-dai-cells = <0>; interrupt-parent = <&gpio1>; interrupts = <14>; - reset-gpio = <&gpio1 15 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; shutdown-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>; ti,imon-slot-no = <0>; ti,vmon-slot-no = <2>; diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index 55b845d38236..f75f08763941 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -2376,6 +2376,13 @@ quirk_flags Skip the probe-time interface setup (usb_set_interface, init_pitch, init_sample_rate); redundant with snd_usb_endpoint_prepare() at stream-open time + * bit 27: ``mixer_playback_linear_vol`` + Set linear volume mapping for devices where the playback volume + control value is mapped to voltage (instead of dB) level linearly. + In short: ``x(raw) = (raw - raw_min) / (raw_max - raw_min)``; + ``V(x) = k * x``; ``dB(x) = 20 * log10(x)``. Overrides bit 24 + * bit 28: ``mixer_capture_linear_vol`` + Similar to bit 27 but for capture streams. Overrides bit 25 This module supports multiple devices, autoprobe and hotplugging. diff --git a/arch/arm/mach-s3c/mach-crag6410-module.c b/arch/arm/mach-s3c/mach-crag6410-module.c index 4ffcf024b09d..14b0f9cc103e 100644 --- a/arch/arm/mach-s3c/mach-crag6410-module.c +++ b/arch/arm/mach-s3c/mach-crag6410-module.c @@ -239,7 +239,6 @@ static struct gpiod_lookup_table wm8994_gpiod_table = { static struct arizona_pdata wm5102_reva_pdata = { .gpio_base = CODEC_GPIO_BASE, .irq_flags = IRQF_TRIGGER_HIGH, - .micd_pol_gpio = CODEC_GPIO_BASE + 4, .micd_rate = 6, .gpio_defaults = { [2] = 0x10000, /* AIF3TXLRCLK */ @@ -265,6 +264,8 @@ static struct gpiod_lookup_table wm5102_reva_gpiod_table = { .table = { GPIO_LOOKUP("GPION", 7, "wlf,ldoena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("arizona", 4, + "wlf,micd-pol", GPIO_ACTIVE_HIGH), { }, }, }; @@ -272,7 +273,6 @@ static struct gpiod_lookup_table wm5102_reva_gpiod_table = { static struct arizona_pdata wm5102_pdata = { .gpio_base = CODEC_GPIO_BASE, .irq_flags = IRQF_TRIGGER_HIGH, - .micd_pol_gpio = CODEC_GPIO_BASE + 2, .gpio_defaults = { [2] = 0x10000, /* AIF3TXLRCLK */ [3] = 0x4, /* OPCLK */ @@ -297,6 +297,8 @@ static struct gpiod_lookup_table wm5102_gpiod_table = { .table = { GPIO_LOOKUP("GPION", 7, "wlf,ldo1ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("arizona", 2, + "wlf,micd-pol", GPIO_ACTIVE_HIGH), { }, }, }; diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 5d8be0ac7c5e..bb93b24c659d 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -9,6 +9,7 @@ * Cirrus Logic International Semiconductor Ltd. */ +#include <kunit/static_stub.h> #include <kunit/visibility.h> #include <linux/cleanup.h> #include <linux/ctype.h> @@ -18,6 +19,7 @@ #include <linux/minmax.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/ratelimit.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/vmalloc.h> @@ -30,45 +32,47 @@ /* * When the KUnit test is running the error-case tests will cause a lot * of messages. Rate-limit to prevent overflowing the kernel log buffer - * during KUnit test runs. + * during KUnit test runs and allow the test to redirect this function. + * In normal (not KUnit) builds this collapses to only return true. */ -#if IS_ENABLED(CONFIG_FW_CS_DSP_KUNIT_TEST) -bool cs_dsp_suppress_err_messages; -EXPORT_SYMBOL_IF_KUNIT(cs_dsp_suppress_err_messages); +VISIBLE_IF_KUNIT bool cs_dsp_can_emit_message(void) +{ + KUNIT_STATIC_STUB_REDIRECT(cs_dsp_can_emit_message); -bool cs_dsp_suppress_warn_messages; -EXPORT_SYMBOL_IF_KUNIT(cs_dsp_suppress_warn_messages); + if (IS_ENABLED(CONFIG_FW_CS_DSP_KUNIT_TEST)) { + static DEFINE_RATELIMIT_STATE(_rs, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + return __ratelimit(&_rs); + } -bool cs_dsp_suppress_info_messages; -EXPORT_SYMBOL_IF_KUNIT(cs_dsp_suppress_info_messages); + return true; +} +EXPORT_SYMBOL_IF_KUNIT(cs_dsp_can_emit_message); -#define cs_dsp_err(_dsp, fmt, ...) \ - do { \ - if (!cs_dsp_suppress_err_messages) \ - dev_err_ratelimited(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__); \ +#define cs_dsp_err(_dsp, fmt, ...) \ + do { \ + if (cs_dsp_can_emit_message()) \ + dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__); \ } while (false) -#define cs_dsp_warn(_dsp, fmt, ...) \ - do { \ - if (!cs_dsp_suppress_warn_messages) \ - dev_warn_ratelimited(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__); \ + +#define cs_dsp_warn(_dsp, fmt, ...) \ + do { \ + if (cs_dsp_can_emit_message()) \ + dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__); \ } while (false) -#define cs_dsp_info(_dsp, fmt, ...) \ - do { \ - if (!cs_dsp_suppress_info_messages) \ - dev_info_ratelimited(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__); \ + +#define cs_dsp_info(_dsp, fmt, ...) \ + do { \ + if (cs_dsp_can_emit_message()) \ + dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__); \ + } while (false) + +#define cs_dsp_dbg(_dsp, fmt, ...) \ + do { \ + if (cs_dsp_can_emit_message()) \ + dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__); \ } while (false) -#define cs_dsp_dbg(_dsp, fmt, ...) \ - dev_dbg_ratelimited(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) -#else -#define cs_dsp_err(_dsp, fmt, ...) \ - dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) -#define cs_dsp_warn(_dsp, fmt, ...) \ - dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) -#define cs_dsp_info(_dsp, fmt, ...) \ - dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) -#define cs_dsp_dbg(_dsp, fmt, ...) \ - dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) -#endif #define ADSP1_CONTROL_1 0x00 #define ADSP1_CONTROL_2 0x02 @@ -515,6 +519,7 @@ void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) debugfs_create_bool("booted", 0444, root, &dsp->booted); debugfs_create_bool("running", 0444, root, &dsp->running); + debugfs_create_bool("hibernating", 0444, root, &dsp->hibernating); debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); @@ -703,7 +708,7 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int lockdep_assert_held(&dsp->pwr_lock); - if (!dsp->running) + if (!dsp->running || dsp->hibernating) return -EPERM; ret = cs_dsp_coeff_base_reg(ctl, ®, 0); @@ -827,7 +832,7 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, } ctl->set = 1; - if (ctl->enabled && ctl->dsp->running) + if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating) ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len); if (ret < 0) @@ -920,12 +925,12 @@ int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, return -EINVAL; if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { - if (ctl->enabled && ctl->dsp->running) + if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating) return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len); else return -EPERM; } else { - if (!ctl->flags && ctl->enabled && ctl->dsp->running) + if (!ctl->flags && ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating) ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); if (buf != ctl->cache) @@ -1108,6 +1113,44 @@ err_ctl: return ret; } + +/** + * cs_dsp_hibernate() - Disable or enable all controls for a DSP + * @dsp: pointer to DSP structure + * @hibernate: whether to set controls to cache only mode + * + * When @hibernate is true, the DSP is entering hibernation mode where the + * regmap is inaccessible, and all controls become cache only. + * When @hibernate is false, the DSP has exited hibernation mode. If the DSP + * is running, all controls are re-synced to the DSP. + * + */ +void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernate) +{ + mutex_lock(&dsp->pwr_lock); + + if (!dsp->running) { + cs_dsp_dbg(dsp, "Cannot hibernate, DSP not running\n"); + goto out; + } + + if (dsp->hibernating == hibernate) + goto out; + + cs_dsp_dbg(dsp, "Set hibernating to %d\n", hibernate); + dsp->hibernating = hibernate; + + if (!dsp->hibernating && dsp->running) { + int ret = cs_dsp_coeff_sync_controls(dsp); + + if (ret) + cs_dsp_err(dsp, "Error syncing controls: %d\n", ret); + } +out: + mutex_unlock(&dsp->pwr_lock); +} +EXPORT_SYMBOL_NS_GPL(cs_dsp_hibernate, "FW_CS_DSP"); + struct cs_dsp_coeff_parsed_alg { int id; const u8 *name; @@ -2510,6 +2553,7 @@ int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, goto err_ena; dsp->booted = true; + dsp->hibernating = false; /* Start the core running */ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, @@ -2788,6 +2832,7 @@ int cs_dsp_power_up(struct cs_dsp *dsp, dsp->ops->disable_core(dsp); dsp->booted = true; + dsp->hibernating = false; mutex_unlock(&dsp->pwr_lock); diff --git a/drivers/firmware/cirrus/cs_dsp.h b/drivers/firmware/cirrus/cs_dsp.h index adf543004aea..04d768d08d03 100644 --- a/drivers/firmware/cirrus/cs_dsp.h +++ b/drivers/firmware/cirrus/cs_dsp.h @@ -10,9 +10,7 @@ #define FW_CS_DSP_H #if IS_ENABLED(CONFIG_KUNIT) -extern bool cs_dsp_suppress_err_messages; -extern bool cs_dsp_suppress_warn_messages; -extern bool cs_dsp_suppress_info_messages; +bool cs_dsp_can_emit_message(void); #endif #endif /* ifndef FW_CS_DSP_H */ diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c index 66140caeebb5..63416838f865 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c @@ -7,6 +7,7 @@ #include <kunit/device.h> #include <kunit/resource.h> +#include <kunit/static_stub.h> #include <kunit/test.h> #include <linux/build_bug.h> #include <linux/firmware/cirrus/cs_dsp.h> @@ -2155,6 +2156,15 @@ static void bin_patch_name_and_info(struct kunit *test) KUNIT_EXPECT_EQ(test, reg_val, payload_data); } +static bool cs_dsp_bin_test_can_emit_message_hook(void) +{ +#if defined(DEBUG) + return true; +#else + return false; +#endif +} + static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp, int wmdr_ver) { @@ -2239,16 +2249,12 @@ static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp, * The large number of test cases will cause an unusually large amount * of dev_info() messages from cs_dsp, so suppress these. */ - cs_dsp_suppress_info_messages = true; + kunit_activate_static_stub(test, cs_dsp_can_emit_message, + cs_dsp_bin_test_can_emit_message_hook); return 0; } -static void cs_dsp_bin_test_exit(struct kunit *test) -{ - cs_dsp_suppress_info_messages = false; -} - static int cs_dsp_bin_test_halo_init_common(struct kunit *test, int wmdr_ver) { struct cs_dsp *dsp; @@ -2833,28 +2839,29 @@ static struct kunit_case cs_dsp_bin_test_cases_adsp2[] = { static struct kunit_suite cs_dsp_bin_test_halo = { .name = "cs_dsp_bin_halo", .init = cs_dsp_bin_test_halo_init, - .exit = cs_dsp_bin_test_exit, .test_cases = cs_dsp_bin_test_cases_halo, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_bin_test_halo_wmdr3 = { .name = "cs_dsp_bin_halo_wmdr_v3", .init = cs_dsp_bin_test_halo_wmdr3_init, .test_cases = cs_dsp_bin_test_cases_halo_wmdr3, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_bin_test_adsp2_32bit = { .name = "cs_dsp_bin_adsp2_32bit", .init = cs_dsp_bin_test_adsp2_32bit_init, - .exit = cs_dsp_bin_test_exit, .test_cases = cs_dsp_bin_test_cases_adsp2, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_bin_test_adsp2_16bit = { .name = "cs_dsp_bin_adsp2_16bit", .init = cs_dsp_bin_test_adsp2_16bit_init, - .exit = cs_dsp_bin_test_exit, .test_cases = cs_dsp_bin_test_cases_adsp2, + .attr.speed = KUNIT_SPEED_SLOW, }; kunit_test_suites(&cs_dsp_bin_test_halo, diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c index 9b2763b36970..f879c5467c98 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c @@ -8,6 +8,7 @@ #include <kunit/device.h> #include <kunit/resource.h> +#include <kunit/static_stub.h> #include <kunit/test.h> #include <linux/build_bug.h> #include <linux/firmware/cirrus/cs_dsp.h> @@ -380,11 +381,13 @@ static void bin_block_payload_len_garbage(struct kunit *test) 0); } -static void cs_dsp_bin_err_test_exit(struct kunit *test) +static bool cs_dsp_bin_err_test_can_emit_message_hook(void) { - cs_dsp_suppress_err_messages = false; - cs_dsp_suppress_warn_messages = false; - cs_dsp_suppress_info_messages = false; +#if defined(DEBUG) + return true; +#else + return false; +#endif } static int cs_dsp_bin_err_test_common_init(struct kunit *test, struct cs_dsp *dsp, @@ -482,9 +485,8 @@ static int cs_dsp_bin_err_test_common_init(struct kunit *test, struct cs_dsp *ds * Testing error conditions can produce a lot of log output * from cs_dsp error messages, so suppress messages. */ - cs_dsp_suppress_err_messages = true; - cs_dsp_suppress_warn_messages = true; - cs_dsp_suppress_info_messages = true; + kunit_activate_static_stub(test, cs_dsp_can_emit_message, + cs_dsp_bin_err_test_can_emit_message_hook); return 0; } @@ -584,22 +586,22 @@ static struct kunit_case cs_dsp_bin_err_test_cases[] = { static struct kunit_suite cs_dsp_bin_err_test_halo = { .name = "cs_dsp_bin_err_halo", .init = cs_dsp_bin_err_test_halo_init, - .exit = cs_dsp_bin_err_test_exit, .test_cases = cs_dsp_bin_err_test_cases, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_bin_err_test_adsp2_32bit = { .name = "cs_dsp_bin_err_adsp2_32bit", .init = cs_dsp_bin_err_test_adsp2_32bit_init, - .exit = cs_dsp_bin_err_test_exit, .test_cases = cs_dsp_bin_err_test_cases, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_bin_err_test_adsp2_16bit = { .name = "cs_dsp_bin_err_adsp2_16bit", .init = cs_dsp_bin_err_test_adsp2_16bit_init, - .exit = cs_dsp_bin_err_test_exit, .test_cases = cs_dsp_bin_err_test_cases, + .attr.speed = KUNIT_SPEED_SLOW, }; kunit_test_suites(&cs_dsp_bin_err_test_halo, diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c b/drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c index ebca3a4ab0f1..b03cd7c37f6e 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c @@ -3248,30 +3248,35 @@ static struct kunit_suite cs_dsp_ctl_cache_test_halo = { .name = "cs_dsp_ctl_cache_wmfwV3_halo", .init = cs_dsp_ctl_cache_test_halo_init, .test_cases = cs_dsp_ctl_cache_test_cases_v3, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_cache_test_adsp2_32bit_wmfw1 = { .name = "cs_dsp_ctl_cache_wmfwV1_adsp2_32bit", .init = cs_dsp_ctl_cache_test_adsp2_32bit_wmfw1_init, .test_cases = cs_dsp_ctl_cache_test_cases_v1, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_cache_test_adsp2_32bit_wmfw2 = { .name = "cs_dsp_ctl_cache_wmfwV2_adsp2_32bit", .init = cs_dsp_ctl_cache_test_adsp2_32bit_wmfw2_init, .test_cases = cs_dsp_ctl_cache_test_cases_v2, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_cache_test_adsp2_16bit_wmfw1 = { .name = "cs_dsp_ctl_cache_wmfwV1_adsp2_16bit", .init = cs_dsp_ctl_cache_test_adsp2_16bit_wmfw1_init, .test_cases = cs_dsp_ctl_cache_test_cases_v1, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_cache_test_adsp2_16bit_wmfw2 = { .name = "cs_dsp_ctl_cache_wmfwV2_adsp2_16bit", .init = cs_dsp_ctl_cache_test_adsp2_16bit_wmfw2_init, .test_cases = cs_dsp_ctl_cache_test_cases_v2, + .attr.speed = KUNIT_SPEED_SLOW, }; kunit_test_suites(&cs_dsp_ctl_cache_test_halo, diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c b/drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c index 942ba1af5e7c..368703767644 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c @@ -1805,30 +1805,35 @@ static struct kunit_suite cs_dsp_ctl_parse_test_halo = { .name = "cs_dsp_ctl_parse_wmfwV3_halo", .init = cs_dsp_ctl_parse_test_halo_init, .test_cases = cs_dsp_ctl_parse_test_cases_v2_v3, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_parse_test_adsp2_32bit_wmfw1 = { .name = "cs_dsp_ctl_parse_wmfwV1_adsp2_32bit", .init = cs_dsp_ctl_parse_test_adsp2_32bit_wmfw1_init, .test_cases = cs_dsp_ctl_parse_test_cases_v1, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_parse_test_adsp2_32bit_wmfw2 = { .name = "cs_dsp_ctl_parse_wmfwV2_adsp2_32bit", .init = cs_dsp_ctl_parse_test_adsp2_32bit_wmfw2_init, .test_cases = cs_dsp_ctl_parse_test_cases_v2_v3, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_parse_test_adsp2_16bit_wmfw1 = { .name = "cs_dsp_ctl_parse_wmfwV1_adsp2_16bit", .init = cs_dsp_ctl_parse_test_adsp2_16bit_wmfw1_init, .test_cases = cs_dsp_ctl_parse_test_cases_v1, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_parse_test_adsp2_16bit_wmfw2 = { .name = "cs_dsp_ctl_parse_wmfwV2_adsp2_16bit", .init = cs_dsp_ctl_parse_test_adsp2_16bit_wmfw2_init, .test_cases = cs_dsp_ctl_parse_test_cases_v2_v3, + .attr.speed = KUNIT_SPEED_SLOW, }; kunit_test_suites(&cs_dsp_ctl_parse_test_halo, diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c b/drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c index bda00a95d4f9..d800f163b099 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c @@ -2636,30 +2636,35 @@ static struct kunit_suite cs_dsp_ctl_rw_test_halo = { .name = "cs_dsp_ctl_rw_wmfwV3_halo", .init = cs_dsp_ctl_rw_test_halo_init, .test_cases = cs_dsp_ctl_rw_test_cases_halo, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_rw_test_adsp2_32bit_wmfw1 = { .name = "cs_dsp_ctl_rw_wmfwV1_adsp2_32bit", .init = cs_dsp_ctl_rw_test_adsp2_32bit_wmfw1_init, .test_cases = cs_dsp_ctl_rw_test_cases_adsp, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_rw_test_adsp2_32bit_wmfw2 = { .name = "cs_dsp_ctl_rw_wmfwV2_adsp2_32bit", .init = cs_dsp_ctl_rw_test_adsp2_32bit_wmfw2_init, .test_cases = cs_dsp_ctl_rw_test_cases_adsp, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_rw_test_adsp2_16bit_wmfw1 = { .name = "cs_dsp_ctl_rw_wmfwV1_adsp2_16bit", .init = cs_dsp_ctl_rw_test_adsp2_16bit_wmfw1_init, .test_cases = cs_dsp_ctl_rw_test_cases_adsp, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_ctl_rw_test_adsp2_16bit_wmfw2 = { .name = "cs_dsp_ctl_rw_wmfwV2_adsp2_16bit", .init = cs_dsp_ctl_rw_test_adsp2_16bit_wmfw2_init, .test_cases = cs_dsp_ctl_rw_test_cases_adsp, + .attr.speed = KUNIT_SPEED_SLOW, }; kunit_test_suites(&cs_dsp_ctl_rw_test_halo, diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c b/drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c index f02cb6cf7638..1de70e81a868 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c @@ -8,6 +8,7 @@ #include <kunit/device.h> #include <kunit/resource.h> +#include <kunit/static_stub.h> #include <kunit/test.h> #include <linux/build_bug.h> #include <linux/firmware/cirrus/cs_dsp.h> @@ -1775,6 +1776,15 @@ static void wmfw_load_with_info(struct kunit *test) KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes); } +static bool cs_dsp_wmfw_test_can_emit_message_hook(void) +{ +#if defined(DEBUG) + return true; +#else + return false; +#endif +} + static int cs_dsp_wmfw_test_common_init(struct kunit *test, struct cs_dsp *dsp, int wmfw_version) { @@ -1863,16 +1873,12 @@ static int cs_dsp_wmfw_test_common_init(struct kunit *test, struct cs_dsp *dsp, * The large number of test cases will cause an unusually large amount * of dev_info() messages from cs_dsp, so suppress these. */ - cs_dsp_suppress_info_messages = true; + kunit_activate_static_stub(test, cs_dsp_can_emit_message, + cs_dsp_wmfw_test_can_emit_message_hook); return 0; } -static void cs_dsp_wmfw_test_exit(struct kunit *test) -{ - cs_dsp_suppress_info_messages = false; -} - static int cs_dsp_wmfw_test_halo_init(struct kunit *test) { struct cs_dsp *dsp; @@ -2180,50 +2186,50 @@ static struct kunit_case cs_dsp_wmfw_test_cases_adsp2[] = { static struct kunit_suite cs_dsp_wmfw_test_halo = { .name = "cs_dsp_wmfwV3_halo", .init = cs_dsp_wmfw_test_halo_init, - .exit = cs_dsp_wmfw_test_exit, .test_cases = cs_dsp_wmfw_test_cases_halo, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_test_adsp2_32bit_wmfw0 = { .name = "cs_dsp_wmfwV0_adsp2_32bit", .init = cs_dsp_wmfw_test_adsp2_32bit_wmfw0_init, - .exit = cs_dsp_wmfw_test_exit, .test_cases = cs_dsp_wmfw_test_cases_adsp2, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_test_adsp2_32bit_wmfw1 = { .name = "cs_dsp_wmfwV1_adsp2_32bit", .init = cs_dsp_wmfw_test_adsp2_32bit_wmfw1_init, - .exit = cs_dsp_wmfw_test_exit, .test_cases = cs_dsp_wmfw_test_cases_adsp2, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_test_adsp2_32bit_wmfw2 = { .name = "cs_dsp_wmfwV2_adsp2_32bit", .init = cs_dsp_wmfw_test_adsp2_32bit_wmfw2_init, - .exit = cs_dsp_wmfw_test_exit, .test_cases = cs_dsp_wmfw_test_cases_adsp2, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_test_adsp2_16bit_wmfw0 = { .name = "cs_dsp_wmfwV0_adsp2_16bit", .init = cs_dsp_wmfw_test_adsp2_16bit_wmfw0_init, - .exit = cs_dsp_wmfw_test_exit, .test_cases = cs_dsp_wmfw_test_cases_adsp2, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_test_adsp2_16bit_wmfw1 = { .name = "cs_dsp_wmfwV1_adsp2_16bit", .init = cs_dsp_wmfw_test_adsp2_16bit_wmfw1_init, - .exit = cs_dsp_wmfw_test_exit, .test_cases = cs_dsp_wmfw_test_cases_adsp2, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_test_adsp2_16bit_wmfw2 = { .name = "cs_dsp_wmfwV2_adsp2_16bit", .init = cs_dsp_wmfw_test_adsp2_16bit_wmfw2_init, - .exit = cs_dsp_wmfw_test_exit, .test_cases = cs_dsp_wmfw_test_cases_adsp2, + .attr.speed = KUNIT_SPEED_SLOW, }; kunit_test_suites(&cs_dsp_wmfw_test_halo, diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c b/drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c index 37162d12e2fa..e7bf5dc474f5 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c @@ -8,6 +8,7 @@ #include <kunit/device.h> #include <kunit/resource.h> +#include <kunit/static_stub.h> #include <kunit/test.h> #include <linux/build_bug.h> #include <linux/firmware/cirrus/cs_dsp.h> @@ -989,11 +990,13 @@ static void wmfw_v2_coeff_description_exceeds_block(struct kunit *test) -EOVERFLOW); } -static void cs_dsp_wmfw_err_test_exit(struct kunit *test) +static bool cs_dsp_wmfw_err_test_can_emit_message_hook(void) { - cs_dsp_suppress_err_messages = false; - cs_dsp_suppress_warn_messages = false; - cs_dsp_suppress_info_messages = false; +#if defined(DEBUG) + return true; +#else + return false; +#endif } static int cs_dsp_wmfw_err_test_common_init(struct kunit *test, struct cs_dsp *dsp, @@ -1080,9 +1083,8 @@ static int cs_dsp_wmfw_err_test_common_init(struct kunit *test, struct cs_dsp *d * Testing error conditions can produce a lot of log output * from cs_dsp error messages, so suppress messages. */ - cs_dsp_suppress_err_messages = true; - cs_dsp_suppress_warn_messages = true; - cs_dsp_suppress_info_messages = true; + kunit_activate_static_stub(test, cs_dsp_can_emit_message, + cs_dsp_wmfw_err_test_can_emit_message_hook); return 0; } @@ -1304,50 +1306,50 @@ static struct kunit_case cs_dsp_wmfw_err_test_cases_v3[] = { static struct kunit_suite cs_dsp_wmfw_err_test_halo = { .name = "cs_dsp_wmfwV3_err_halo", .init = cs_dsp_wmfw_err_test_halo_init, - .exit = cs_dsp_wmfw_err_test_exit, .test_cases = cs_dsp_wmfw_err_test_cases_v3, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_32bit_wmfw0 = { .name = "cs_dsp_wmfwV0_err_adsp2_32bit", .init = cs_dsp_wmfw_err_test_adsp2_32bit_wmfw0_init, - .exit = cs_dsp_wmfw_err_test_exit, .test_cases = cs_dsp_wmfw_err_test_cases_v0, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_32bit_wmfw1 = { .name = "cs_dsp_wmfwV1_err_adsp2_32bit", .init = cs_dsp_wmfw_err_test_adsp2_32bit_wmfw1_init, - .exit = cs_dsp_wmfw_err_test_exit, .test_cases = cs_dsp_wmfw_err_test_cases_v1, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_32bit_wmfw2 = { .name = "cs_dsp_wmfwV2_err_adsp2_32bit", .init = cs_dsp_wmfw_err_test_adsp2_32bit_wmfw2_init, - .exit = cs_dsp_wmfw_err_test_exit, .test_cases = cs_dsp_wmfw_err_test_cases_v2, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_16bit_wmfw0 = { .name = "cs_dsp_wmfwV0_err_adsp2_16bit", .init = cs_dsp_wmfw_err_test_adsp2_16bit_wmfw0_init, - .exit = cs_dsp_wmfw_err_test_exit, .test_cases = cs_dsp_wmfw_err_test_cases_v0, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_16bit_wmfw1 = { .name = "cs_dsp_wmfwV1_err_adsp2_16bit", .init = cs_dsp_wmfw_err_test_adsp2_16bit_wmfw1_init, - .exit = cs_dsp_wmfw_err_test_exit, .test_cases = cs_dsp_wmfw_err_test_cases_v1, + .attr.speed = KUNIT_SPEED_SLOW, }; static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_16bit_wmfw2 = { .name = "cs_dsp_wmfwV2_err_adsp2_16bit", .init = cs_dsp_wmfw_err_test_adsp2_16bit_wmfw2_init, - .exit = cs_dsp_wmfw_err_test_exit, .test_cases = cs_dsp_wmfw_err_test_cases_v2, + .attr.speed = KUNIT_SPEED_SLOW, }; kunit_test_suites(&cs_dsp_wmfw_err_test_halo, diff --git a/drivers/mfd/cs42l43-i2c.c b/drivers/mfd/cs42l43-i2c.c index a2ab001a600a..0a0ab5e549a5 100644 --- a/drivers/mfd/cs42l43-i2c.c +++ b/drivers/mfd/cs42l43-i2c.c @@ -47,6 +47,7 @@ static int cs42l43_i2c_probe(struct i2c_client *i2c) cs42l43->irq = i2c->irq; /* A device on an I2C is always attached by definition. */ cs42l43->attached = true; + cs42l43->variant_id = (long)device_get_match_data(cs42l43->dev); cs42l43->regmap = devm_regmap_init_i2c(i2c, &cs42l43_i2c_regmap); if (IS_ERR(cs42l43->regmap)) @@ -58,7 +59,8 @@ static int cs42l43_i2c_probe(struct i2c_client *i2c) #if IS_ENABLED(CONFIG_OF) static const struct of_device_id cs42l43_of_match[] = { - { .compatible = "cirrus,cs42l43", }, + { .compatible = "cirrus,cs42l43", .data = (void *)CS42L43_DEVID_VAL }, + { .compatible = "cirrus,cs42l43b", .data = (void *)CS42L43B_DEVID_VAL }, {} }; MODULE_DEVICE_TABLE(of, cs42l43_of_match); @@ -66,7 +68,8 @@ MODULE_DEVICE_TABLE(of, cs42l43_of_match); #if IS_ENABLED(CONFIG_ACPI) static const struct acpi_device_id cs42l43_acpi_match[] = { - { "CSC4243", 0 }, + { "CSC4243", CS42L43_DEVID_VAL }, + { "CSC2A3B", CS42L43B_DEVID_VAL }, {} }; MODULE_DEVICE_TABLE(acpi, cs42l43_acpi_match); diff --git a/drivers/mfd/cs42l43-sdw.c b/drivers/mfd/cs42l43-sdw.c index 023f7e1a30f8..794c98378175 100644 --- a/drivers/mfd/cs42l43-sdw.c +++ b/drivers/mfd/cs42l43-sdw.c @@ -178,6 +178,7 @@ static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id * cs42l43->dev = dev; cs42l43->sdw = sdw; + cs42l43->variant_id = (long)id->driver_data; cs42l43->regmap = devm_regmap_init_sdw(sdw, &cs42l43_sdw_regmap); if (IS_ERR(cs42l43->regmap)) @@ -188,7 +189,8 @@ static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id * } static const struct sdw_device_id cs42l43_sdw_id[] = { - SDW_SLAVE_ENTRY(0x01FA, 0x4243, 0), + SDW_SLAVE_ENTRY(0x01FA, 0x4243, (void *) CS42L43_DEVID_VAL), + SDW_SLAVE_ENTRY(0x01FA, 0x2A3B, (void *) CS42L43B_DEVID_VAL), {} }; MODULE_DEVICE_TABLE(sdw, cs42l43_sdw_id); diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c index 107cfb983fec..166881751e69 100644 --- a/drivers/mfd/cs42l43.c +++ b/drivers/mfd/cs42l43.c @@ -115,9 +115,14 @@ const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = { { CS42L43_DECIM_HPF_WNF_CTRL2, 0x00000001 }, { CS42L43_DECIM_HPF_WNF_CTRL3, 0x00000001 }, { CS42L43_DECIM_HPF_WNF_CTRL4, 0x00000001 }, + { CS42L43B_DECIM_HPF_WNF_CTRL5, 0x00000001 }, + { CS42L43B_DECIM_HPF_WNF_CTRL6, 0x00000001 }, { CS42L43_DMIC_PDM_CTRL, 0x00000000 }, { CS42L43_DECIM_VOL_CTRL_CH1_CH2, 0x20122012 }, { CS42L43_DECIM_VOL_CTRL_CH3_CH4, 0x20122012 }, + { CS42L43B_DECIM_VOL_CTRL_CH1_CH2, 0x20122012 }, + { CS42L43B_DECIM_VOL_CTRL_CH3_CH4, 0x20122012 }, + { CS42L43B_DECIM_VOL_CTRL_CH5_CH6, 0x20122012 }, { CS42L43_INTP_VOLUME_CTRL1, 0x00000180 }, { CS42L43_INTP_VOLUME_CTRL2, 0x00000180 }, { CS42L43_AMP1_2_VOL_RAMP, 0x00000022 }, @@ -155,8 +160,12 @@ const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = { { CS42L43_SWIRE_DP2_CH2_INPUT, 0x00000000 }, { CS42L43_SWIRE_DP3_CH1_INPUT, 0x00000000 }, { CS42L43_SWIRE_DP3_CH2_INPUT, 0x00000000 }, + { CS42L43B_SWIRE_DP3_CH3_INPUT, 0x00000000 }, + { CS42L43B_SWIRE_DP3_CH4_INPUT, 0x00000000 }, { CS42L43_SWIRE_DP4_CH1_INPUT, 0x00000000 }, { CS42L43_SWIRE_DP4_CH2_INPUT, 0x00000000 }, + { CS42L43B_SWIRE_DP4_CH3_INPUT, 0x00000000 }, + { CS42L43B_SWIRE_DP4_CH4_INPUT, 0x00000000 }, { CS42L43_ASRC_INT1_INPUT1, 0x00000000 }, { CS42L43_ASRC_INT2_INPUT1, 0x00000000 }, { CS42L43_ASRC_INT3_INPUT1, 0x00000000 }, @@ -169,10 +178,14 @@ const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = { { CS42L43_ISRC1INT2_INPUT1, 0x00000000 }, { CS42L43_ISRC1DEC1_INPUT1, 0x00000000 }, { CS42L43_ISRC1DEC2_INPUT1, 0x00000000 }, + { CS42L43B_ISRC1DEC3_INPUT1, 0x00000000 }, + { CS42L43B_ISRC1DEC4_INPUT1, 0x00000000 }, { CS42L43_ISRC2INT1_INPUT1, 0x00000000 }, { CS42L43_ISRC2INT2_INPUT1, 0x00000000 }, { CS42L43_ISRC2DEC1_INPUT1, 0x00000000 }, { CS42L43_ISRC2DEC2_INPUT1, 0x00000000 }, + { CS42L43B_ISRC2DEC3_INPUT1, 0x00000000 }, + { CS42L43B_ISRC2DEC4_INPUT1, 0x00000000 }, { CS42L43_EQ1MIX_INPUT1, 0x00800000 }, { CS42L43_EQ1MIX_INPUT2, 0x00800000 }, { CS42L43_EQ1MIX_INPUT3, 0x00800000 }, @@ -269,6 +282,8 @@ EXPORT_SYMBOL_NS_GPL(cs42l43_reg_default, "MFD_CS42L43"); bool cs42l43_readable_register(struct device *dev, unsigned int reg) { + struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + switch (reg) { case CS42L43_DEVID: case CS42L43_REVID: @@ -292,7 +307,6 @@ bool cs42l43_readable_register(struct device *dev, unsigned int reg) case CS42L43_ADC_B_CTRL1 ... CS42L43_ADC_B_CTRL2: case CS42L43_DECIM_HPF_WNF_CTRL1 ... CS42L43_DECIM_HPF_WNF_CTRL4: case CS42L43_DMIC_PDM_CTRL: - case CS42L43_DECIM_VOL_CTRL_CH1_CH2 ... CS42L43_DECIM_VOL_CTRL_CH3_CH4: case CS42L43_INTP_VOLUME_CTRL1 ... CS42L43_INTP_VOLUME_CTRL2: case CS42L43_AMP1_2_VOL_RAMP: case CS42L43_ASP_CTRL: @@ -387,8 +401,16 @@ bool cs42l43_readable_register(struct device *dev, unsigned int reg) case CS42L43_BOOT_CONTROL: case CS42L43_BLOCK_EN: case CS42L43_SHUTTER_CONTROL: - case CS42L43_MCU_SW_REV ... CS42L43_MCU_RAM_MAX: - return true; + case CS42L43B_MCU_SW_REV ... CS42L43B_MCU_RAM_MAX: + return true; // registers present on all variants + case CS42L43_MCU_SW_REV ... CS42L43B_MCU_SW_REV - 1: + case CS42L43B_MCU_RAM_MAX + 1 ... CS42L43_MCU_RAM_MAX: + case CS42L43_DECIM_VOL_CTRL_CH1_CH2 ... CS42L43_DECIM_VOL_CTRL_CH3_CH4: + return cs42l43->variant_id == CS42L43_DEVID_VAL; // regs only in CS42L43 variant + case CS42L43B_DECIM_VOL_CTRL_CH1_CH2 ... CS42L43B_DECIM_HPF_WNF_CTRL6: + case CS42L43B_SWIRE_DP3_CH3_INPUT ... CS42L43B_SWIRE_DP4_CH4_INPUT: + case CS42L43B_ISRC1DEC3_INPUT1 ... CS42L43B_ISRC2DEC4_INPUT1: + return cs42l43->variant_id == CS42L43B_DEVID_VAL; // regs only in CS42L43B variant default: return false; } @@ -597,15 +619,27 @@ static int cs42l43_wait_for_attach(struct cs42l43 *cs42l43) static int cs42l43_mcu_stage_2_3(struct cs42l43 *cs42l43, bool shadow) { unsigned int need_reg = CS42L43_NEED_CONFIGS; + unsigned int boot_reg; unsigned int val; int ret; - if (shadow) - need_reg = CS42L43_FW_SH_BOOT_CFG_NEED_CONFIGS; + switch (cs42l43->variant_id) { + case CS42L43_DEVID_VAL: + if (shadow) + need_reg = CS42L43_FW_SH_BOOT_CFG_NEED_CONFIGS; + boot_reg = CS42L43_BOOT_STATUS; + break; + case CS42L43B_DEVID_VAL: + need_reg = CS42L43B_NEED_CONFIGS; + boot_reg = CS42L43B_BOOT_STATUS; + break; + default: + return -EINVAL; + } regmap_write(cs42l43->regmap, need_reg, 0); - ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_BOOT_STATUS, + ret = regmap_read_poll_timeout(cs42l43->regmap, boot_reg, val, (val == CS42L43_MCU_BOOT_STAGE3), CS42L43_MCU_POLL_US, CS42L43_MCU_CMD_TIMEOUT_US); if (ret) { @@ -644,13 +678,25 @@ static int cs42l43_mcu_stage_3_2(struct cs42l43 *cs42l43) */ static int cs42l43_mcu_disable(struct cs42l43 *cs42l43) { - unsigned int val; + unsigned int val, cfg_reg, ctrl_reg; int ret; - regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG, - CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_DISABLE_VAL); - regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_MM_CTRL_SELECTION, - CS42L43_FW_MM_CTRL_MCU_SEL_MASK); + switch (cs42l43->variant_id) { + case CS42L43_DEVID_VAL: + cfg_reg = CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG; + ctrl_reg = CS42L43_FW_MISSION_CTRL_MM_CTRL_SELECTION; + break; + case CS42L43B_DEVID_VAL: + cfg_reg = CS42L43B_FW_MISSION_CTRL_MM_MCU_CFG_REG; + ctrl_reg = CS42L43B_FW_MISSION_CTRL_MM_CTRL_SELECTION; + break; + default: + return -EINVAL; + } + + regmap_write(cs42l43->regmap, cfg_reg, CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_DISABLE_VAL); + regmap_write(cs42l43->regmap, ctrl_reg, CS42L43_FW_MM_CTRL_MCU_SEL_MASK); + regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, CS42L43_CONTROL_IND_MASK); regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, 0); @@ -740,18 +786,32 @@ static int cs42l43_mcu_update_step(struct cs42l43 *cs42l43) { unsigned int mcu_rev, bios_rev, boot_status, secure_cfg; bool patched, shadow; + int boot_status_reg, mcu_sw_rev_reg; int ret; + switch (cs42l43->variant_id) { + case CS42L43_DEVID_VAL: + boot_status_reg = CS42L43_BOOT_STATUS; + mcu_sw_rev_reg = CS42L43_MCU_SW_REV; + break; + case CS42L43B_DEVID_VAL: + boot_status_reg = CS42L43B_BOOT_STATUS; + mcu_sw_rev_reg = CS42L43B_MCU_SW_REV; + break; + default: + return -EINVAL; + } + /* Clear any stale software interrupt bits. */ regmap_read(cs42l43->regmap, CS42L43_SOFT_INT, &mcu_rev); - ret = regmap_read(cs42l43->regmap, CS42L43_BOOT_STATUS, &boot_status); + ret = regmap_read(cs42l43->regmap, boot_status_reg, &boot_status); if (ret) { dev_err(cs42l43->dev, "Failed to read boot status: %d\n", ret); return ret; } - ret = regmap_read(cs42l43->regmap, CS42L43_MCU_SW_REV, &mcu_rev); + ret = regmap_read(cs42l43->regmap, mcu_sw_rev_reg, &mcu_rev); if (ret) { dev_err(cs42l43->dev, "Failed to read firmware revision: %d\n", ret); return ret; @@ -918,6 +978,13 @@ static void cs42l43_boot_work(struct work_struct *work) switch (devid) { case CS42L43_DEVID_VAL: + case CS42L43B_DEVID_VAL: + if (devid != cs42l43->variant_id) { + dev_err(cs42l43->dev, + "Device ID (0x%06x) does not match variant ID (0x%06lx)\n", + devid, cs42l43->variant_id); + goto err; + } break; default: dev_err(cs42l43->dev, "Unrecognised devid: 0x%06x\n", devid); diff --git a/drivers/mfd/cs42l43.h b/drivers/mfd/cs42l43.h index f3da783930f5..a0068f6572e2 100644 --- a/drivers/mfd/cs42l43.h +++ b/drivers/mfd/cs42l43.h @@ -9,7 +9,7 @@ #ifndef CS42L43_CORE_INT_H #define CS42L43_CORE_INT_H -#define CS42L43_N_DEFAULTS 176 +#define CS42L43_N_DEFAULTS 189 struct dev_pm_ops; struct device; diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 6d1ce7f5da51..45850f2d4342 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -140,6 +140,18 @@ #define DISPLAY_PORT_RX_6 134 #define DISPLAY_PORT_RX_7 135 #define USB_RX 136 +#define LPI_MI2S_RX_0 137 +#define LPI_MI2S_TX_0 138 +#define LPI_MI2S_RX_1 139 +#define LPI_MI2S_TX_1 140 +#define LPI_MI2S_RX_2 141 +#define LPI_MI2S_TX_2 142 +#define LPI_MI2S_RX_3 143 +#define LPI_MI2S_TX_3 144 +#define LPI_MI2S_RX_4 145 +#define LPI_MI2S_TX_4 146 +#define SENARY_MI2S_RX 147 +#define SENARY_MI2S_TX 148 #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 0ec1cdc5585d..4e3baa557068 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -179,6 +179,7 @@ struct cs_dsp { bool booted; bool running; + bool hibernating; struct list_head ctl_list; @@ -354,4 +355,6 @@ int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val); int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch); int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits); +void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernating); + #endif diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index f72e6d4b14a7..d465dcd8c90a 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -117,11 +117,6 @@ struct arizona_pdata { /** Check for line output with HPDET method */ bool hpdet_acc_id_line; -#ifdef CONFIG_GPIOLIB_LEGACY - /** GPIO used for mic isolation with HPDET */ - int hpdet_id_gpio; -#endif - /** Channel to use for headphone detection */ unsigned int hpdet_channel; @@ -131,11 +126,6 @@ struct arizona_pdata { /** Extra debounce timeout used during initial mic detection (ms) */ unsigned int micd_detect_debounce; -#ifdef CONFIG_GPIOLIB_LEGACY - /** GPIO for mic detection polarity */ - int micd_pol_gpio; -#endif - /** Mic detect ramp rate */ unsigned int micd_bias_start_time; diff --git a/include/linux/mfd/cs42l43-regs.h b/include/linux/mfd/cs42l43-regs.h index c39a49269cb7..68831f113589 100644 --- a/include/linux/mfd/cs42l43-regs.h +++ b/include/linux/mfd/cs42l43-regs.h @@ -1181,4 +1181,80 @@ /* CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG */ #define CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_DISABLE_VAL 0xF05AA50F +/* CS42L43B VARIANT REGISTERS */ +#define CS42L43B_DEVID_VAL 0x0042A43B + +#define CS42L43B_DECIM_VOL_CTRL_CH1_CH2 0x00008280 +#define CS42L43B_DECIM_VOL_CTRL_CH3_CH4 0x00008284 + +#define CS42L43B_DECIM_VOL_CTRL_CH5_CH6 0x00008290 +#define CS42L43B_DECIM_VOL_CTRL_UPDATE 0x0000829C + +#define CS42L43B_DECIM_HPF_WNF_CTRL5 0x000082A0 +#define CS42L43B_DECIM_HPF_WNF_CTRL6 0x000082A4 + +#define CS42L43B_SWIRE_DP3_CH3_INPUT 0x0000C320 +#define CS42L43B_SWIRE_DP3_CH4_INPUT 0x0000C330 +#define CS42L43B_SWIRE_DP4_CH3_INPUT 0x0000C340 +#define CS42L43B_SWIRE_DP4_CH4_INPUT 0x0000C350 + +#define CS42L43B_ISRC1DEC3_INPUT1 0x0000C780 +#define CS42L43B_ISRC1DEC4_INPUT1 0x0000C790 +#define CS42L43B_ISRC2DEC3_INPUT1 0x0000C7A0 +#define CS42L43B_ISRC2DEC4_INPUT1 0x0000C7B0 + +#define CS42L43B_FW_MISSION_CTRL_NEED_CONFIGS 0x00117E00 +#define CS42L43B_FW_MISSION_CTRL_HAVE_CONFIGS 0x00117E04 +#define CS42L43B_FW_MISSION_CTRL_PATCH_START_ADDR_REG 0x00117E08 +#define CS42L43B_FW_MISSION_CTRL_MM_CTRL_SELECTION 0x00117E0C +#define CS42L43B_FW_MISSION_CTRL_MM_MCU_CFG_REG 0x00117E10 + +#define CS42L43B_MCU_SW_REV 0x00117314 +#define CS42L43B_PATCH_START_ADDR 0x00117318 +#define CS42L43B_CONFIG_SELECTION 0x0011731C +#define CS42L43B_NEED_CONFIGS 0x00117320 +#define CS42L43B_BOOT_STATUS 0x00117330 + +#define CS42L43B_FW_MISSION_CTRL_NEED_CONFIGS 0x00117E00 +#define CS42L43B_FW_MISSION_CTRL_HAVE_CONFIGS 0x00117E04 +#define CS42L43B_FW_MISSION_CTRL_PATCH_START_ADDR_REG 0x00117E08 +#define CS42L43B_FW_MISSION_CTRL_MM_CTRL_SELECTION 0x00117E0C +#define CS42L43B_FW_MISSION_CTRL_MM_MCU_CFG_REG 0x00117E10 + +#define CS42L43B_MCU_RAM_MAX 0x00117FFF + +/* CS42L43B_DECIM_DECIM_VOL_CTRL_CH5_CH6 */ +#define CS42L43B_DECIM6_MUTE_MASK 0x80000000 +#define CS42L43B_DECIM6_MUTE_SHIFT 31 +#define CS42L43B_DECIM6_VOL_MASK 0x3FC00000 +#define CS42L43B_DECIM6_VOL_SHIFT 22 +#define CS42L43B_DECIM6_PATH1_VOL_FALL_RATE_MASK 0x00380000 +#define CS42L43B_DECIM6_PATH1_VOL_FALL_RATE_SHIFT 19 +#define CS42L43B_DECIM6_PATH1_VOL_RISE_RATE_MASK 0x00070000 +#define CS42L43B_DECIM6_PATH1_VOL_RISE_RATE_SHIFT 16 +#define CS42L43B_DECIM5_MUTE_MASK 0x00008000 +#define CS42L43B_DECIM5_MUTE_SHIFT 15 +#define CS42L43B_DECIM5_VOL_MASK 0x00003FC0 +#define CS42L43B_DECIM5_VOL_SHIFT 6 +#define CS42L43B_DECIM5_PATH1_VOL_FALL_RATE_MASK 0x00000038 +#define CS42L43B_DECIM5_PATH1_VOL_FALL_RATE_SHIFT 3 +#define CS42L43B_DECIM5_PATH1_VOL_RISE_RATE_MASK 0x00000007 +#define CS42L43B_DECIM5_PATH1_VOL_RISE_RATE_SHIFT 0 + +/* CS42L43B_DECIM_VOL_CTRL_UPDATE */ +#define CS42L43B_DECIM6_PATH1_VOL_TRIG_MASK 0x00000800 +#define CS42L43B_DECIM6_PATH1_VOL_TRIG_SHIFT 11 +#define CS42L43B_DECIM5_PATH1_VOL_TRIG_MASK 0x00000100 +#define CS42L43B_DECIM5_PATH1_VOL_TRIG_SHIFT 8 +#define CS42L43B_DECIM4_VOL_UPDATE_MASK 0x00000020 +#define CS42L43B_DECIM4_VOL_UPDATE_SHIFT 5 + +/* CS42L43_ISRC1_CTRL..CS42L43_ISRC2_CTRL */ +#define CS42L43B_ISRC_DEC4_EN_MASK 0x00000008 +#define CS42L43B_ISRC_DEC4_EN_SHIFT 3 +#define CS42L43B_ISRC_DEC4_EN_WIDTH 1 +#define CS42L43B_ISRC_DEC3_EN_MASK 0x00000004 +#define CS42L43B_ISRC_DEC3_EN_SHIFT 2 +#define CS42L43B_ISRC_DEC3_EN_WIDTH 1 + #endif /* CS42L43_CORE_REGS_H */ diff --git a/include/linux/mfd/cs42l43.h b/include/linux/mfd/cs42l43.h index 2239d8585e78..ff0f7e365a19 100644 --- a/include/linux/mfd/cs42l43.h +++ b/include/linux/mfd/cs42l43.h @@ -98,6 +98,7 @@ struct cs42l43 { bool sdw_pll_active; bool attached; bool hw_lock; + long variant_id; }; #endif /* CS42L43_CORE_EXT_H */ diff --git a/include/sound/core.h b/include/sound/core.h index 64327e971122..4093ec82a0a1 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -133,6 +133,9 @@ struct snd_card { #ifdef CONFIG_SND_DEBUG struct dentry *debugfs_root; /* debugfs root for card */ #endif +#ifdef CONFIG_SND_CTL_DEBUG + struct snd_ctl_elem_value *value_buf; /* buffer for kctl->put() verification */ +#endif #ifdef CONFIG_PM unsigned int power_state; /* power state */ diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 28f9f5940ab6..c3b10587cb4c 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -32,9 +32,6 @@ struct snd_ctl_elem_value; #define CS35L56_UPDATE_REGS 0x0002A0C #define CS35L56_REFCLK_INPUT 0x0002C04 #define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C -#define CS35L56_OTP_MEM_53 0x00300D4 -#define CS35L56_OTP_MEM_54 0x00300D8 -#define CS35L56_OTP_MEM_55 0x00300DC #define CS35L56_ASP1_ENABLES1 0x0004800 #define CS35L56_ASP1_CONTROL1 0x0004804 #define CS35L56_ASP1_CONTROL2 0x0004808 @@ -86,6 +83,9 @@ struct snd_ctl_elem_value; #define CS35L56_DIE_STS1 0x0017040 #define CS35L56_DIE_STS2 0x0017044 #define CS35L56_DSP_RESTRICT_STS1 0x00190F0 +#define CS35L56_OTP_MEM_53 0x00300D4 +#define CS35L56_OTP_MEM_54 0x00300D8 +#define CS35L56_OTP_MEM_55 0x00300DC #define CS35L56_DSP1_XMEM_PACKED_0 0x2000000 #define CS35L56_DSP1_XMEM_PACKED_6143 0x2005FFC #define CS35L56_DSP1_XMEM_UNPACKED32_0 0x2400000 @@ -435,6 +435,7 @@ ssize_t cs35l56_cal_data_debugfs_read(struct cs35l56_base *cs35l56_base, ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base, const char __user *from, size_t count, loff_t *ppos); +int cs35l56_factory_calibrate(struct cs35l56_base *cs35l56_base); void cs35l56_create_cal_debugfs(struct cs35l56_base *cs35l56_base, const struct cs35l56_cal_debugfs_fops *fops); void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base); diff --git a/include/sound/gus.h b/include/sound/gus.h index 321ae93625eb..3feb42627de1 100644 --- a/include/sound/gus.h +++ b/include/sound/gus.h @@ -536,6 +536,7 @@ int snd_gf1_dma_transfer_block(struct snd_gus_card * gus, struct snd_gf1_dma_block * block, int atomic, int synth); +void snd_gf1_dma_suspend(struct snd_gus_card *gus); /* gus_volume.c */ @@ -552,6 +553,8 @@ struct snd_gus_voice *snd_gf1_alloc_voice(struct snd_gus_card * gus, int type, i void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice); int snd_gf1_start(struct snd_gus_card * gus); int snd_gf1_stop(struct snd_gus_card * gus); +int snd_gf1_suspend(struct snd_gus_card *gus); +int snd_gf1_resume(struct snd_gus_card *gus); /* gus_mixer.c */ @@ -572,6 +575,8 @@ int snd_gus_create(struct snd_card *card, int effect, struct snd_gus_card ** rgus); int snd_gus_initialize(struct snd_gus_card * gus); +int snd_gus_suspend(struct snd_gus_card *gus); +int snd_gus_resume(struct snd_gus_card *gus); /* gus_irq.c */ @@ -583,6 +588,8 @@ void snd_gus_irq_profile_init(struct snd_gus_card *gus); /* gus_uart.c */ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device); +void snd_gf1_uart_suspend(struct snd_gus_card *gus); +void snd_gf1_uart_resume(struct snd_gus_card *gus); /* gus_dram.c */ int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr, @@ -593,5 +600,6 @@ int snd_gus_dram_read(struct snd_gus_card *gus, char __user *ptr, /* gus_timer.c */ void snd_gf1_timers_init(struct snd_gus_card *gus); void snd_gf1_timers_done(struct snd_gus_card *gus); +void snd_gf1_timers_resume(struct snd_gus_card *gus); #endif /* __SOUND_GUS_H */ diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 5d9f0ef228af..24581080e26a 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -336,6 +336,17 @@ snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm); } +/* sync after write */ +static inline int +snd_hda_codec_write_sync(struct hda_codec *codec, hda_nid_t nid, int flags, + unsigned int verb, unsigned int parm) +{ + /* use snd_hda_codec_read() for writing; + * the returned value is usually discarded + */ + return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm); +} + #define snd_hda_param_read(codec, nid, param) \ snd_hdac_read_parm(&(codec)->core, nid, param) #define snd_hda_get_sub_nodes(codec, nid, start_nid) \ @@ -470,6 +481,10 @@ void snd_hda_unlock_devices(struct hda_bus *bus); void snd_hda_bus_reset(struct hda_bus *bus); void snd_hda_bus_reset_codecs(struct hda_bus *bus); +void snd_hda_codec_set_gpio(struct hda_codec *codec, unsigned int mask, + unsigned int dir, unsigned int data, + unsigned int delay); + int snd_hda_codec_set_name(struct hda_codec *codec, const char *name); /* diff --git a/include/sound/hda_verbs.h b/include/sound/hda_verbs.h index 006d358acce2..6066954409aa 100644 --- a/include/sound/hda_verbs.h +++ b/include/sound/hda_verbs.h @@ -56,7 +56,12 @@ enum { #define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d #define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */ #define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f -/* f10-f1a: GPIO */ +/* f10-f1a: GPI/GPO/GPIO */ +#define AC_VERB_GET_GPI_DATA 0x0f10 +#define AC_VERB_GET_GPI_WAKE_MASK 0x0f11 +#define AC_VERB_GET_GPI_UNSOLICITED_RSP_MASK 0x0f12 +#define AC_VERB_GET_GPI_STICKY_MASK 0x0f13 +#define AC_VERB_GET_GPO_DATA 0x0f14 #define AC_VERB_GET_GPIO_DATA 0x0f15 #define AC_VERB_GET_GPIO_MASK 0x0f16 #define AC_VERB_GET_GPIO_DIRECTION 0x0f17 @@ -99,6 +104,11 @@ enum { #define AC_VERB_SET_DIGI_CONVERT_2 0x70e #define AC_VERB_SET_DIGI_CONVERT_3 0x73e #define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f +#define AC_VERB_SET_GPI_DATA 0x710 +#define AC_VERB_SET_GPI_WAKE_MASK 0x711 +#define AC_VERB_SET_SPI_UNSOLICITED_RSP_MASK 0x712 +#define AC_VERB_SET_GPI_STICKY_MASK 0x713 +#define AC_VERB_SET_GPO_DATA 0x714 #define AC_VERB_SET_GPIO_DATA 0x715 #define AC_VERB_SET_GPIO_MASK 0x716 #define AC_VERB_SET_GPIO_DIRECTION 0x717 diff --git a/include/sound/pcm.h b/include/sound/pcm.h index a7860c047503..76fc33dce537 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -729,6 +729,10 @@ static inline void __snd_pcm_set_state(struct snd_pcm_runtime *runtime, runtime->status->state = state; /* copy for mmap */ } +void snd_pcm_set_state(struct snd_pcm_substream *substream, + snd_pcm_state_t state); +snd_pcm_state_t snd_pcm_get_state(struct snd_pcm_substream *substream); + /** * bytes_to_samples - Unit conversion of the size from bytes to samples * @runtime: PCM runtime instance diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index aa9124f93218..46a61a52decc 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -13,6 +13,8 @@ struct device; struct regmap; struct sdca_function_data; +struct snd_ctl_elem_value; +struct snd_kcontrol; struct snd_kcontrol_new; struct snd_pcm_hw_params; struct snd_pcm_substream; @@ -23,6 +25,42 @@ struct snd_soc_dai_ops; struct snd_soc_dapm_route; struct snd_soc_dapm_widget; +/* 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) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = sdca_asoc_q78_get_volsw, \ + .put = sdca_asoc_q78_put_volsw, \ + .private_value = (unsigned long)&(struct soc_mixer_control) { \ + .reg = (xreg), .rreg = (xreg), \ + .min = (xmin), .max = (xmax), \ + .shift = (xstep), .rshift = (xstep), \ + .sign_bit = 15 \ + } \ +} + +/* convenient macro for stereo volume in 7.8 fixed format with separate registers for L/R */ +#define SDCA_DOUBLE_Q78_TLV(xname, xreg_l, xreg_r, xmin, xmax, xstep, tlv_array) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = sdca_asoc_q78_get_volsw, \ + .put = sdca_asoc_q78_put_volsw, \ + .private_value = (unsigned long)&(struct soc_mixer_control) { \ + .reg = (xreg_l), .rreg = (xreg_r), \ + .min = (xmin), .max = (xmax), \ + .shift = (xstep), .rshift = (xstep), \ + .sign_bit = 15 \ + } \ +} + int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *function, int *num_widgets, int *num_routes, int *num_controls, int *num_dais); @@ -57,5 +95,8 @@ int sdca_asoc_hw_params(struct device *dev, struct regmap *regmap, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai); - +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); #endif // __SDCA_ASOC_H__ diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 69a9c9c4d0e9..915e6ae5f68d 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -54,6 +54,11 @@ struct prop_nums { int platforms; }; +enum simple_util_sysclk_order { + SIMPLE_SYSCLK_ORDER_CODEC_FIRST = 0, + SIMPLE_SYSCLK_ORDER_CPU_FIRST, +}; + struct simple_util_priv { struct snd_soc_card snd_card; struct simple_dai_props { @@ -63,6 +68,7 @@ struct simple_util_priv { struct snd_soc_codec_conf *codec_conf; struct prop_nums num; unsigned int mclk_fs; + enum simple_util_sysclk_order sysclk_order; } *dai_props; struct simple_util_jack hp_jack; struct simple_util_jack mic_jack; diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 2a2b74b24a60..7e158d27ae8d 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -86,10 +86,10 @@ struct snd_soc_component_driver { unsigned int reg, unsigned int val); /* pcm creation and destruction */ - int (*pcm_construct)(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd); - void (*pcm_destruct)(struct snd_soc_component *component, - struct snd_pcm *pcm); + int (*pcm_new)(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd); + void (*pcm_free)(struct snd_soc_component *component, + struct snd_pcm *pcm); /* component wide operations */ int (*set_sysclk)(struct snd_soc_component *component, @@ -224,7 +224,6 @@ struct snd_soc_component { int num_dai; struct regmap *regmap; - int val_bytes; struct mutex io_mutex; @@ -327,7 +326,7 @@ int snd_soc_component_stream_event(struct snd_soc_component *component, int snd_soc_component_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level); -void snd_soc_component_setup_regmap(struct snd_soc_component *component); +int snd_soc_component_regmap_val_bytes(struct snd_soc_component *component); #ifdef CONFIG_REGMAP void snd_soc_component_init_regmap(struct snd_soc_component *component, struct regmap *regmap); diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 224396927aef..6a42812bba8c 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -53,6 +53,21 @@ struct snd_compr_stream; #define SND_SOC_POSSIBLE_DAIFMT_PDM (1 << SND_SOC_DAI_FORMAT_PDM) /* + * DAI TDM slot idle modes + * + * Describes a CODEC/CPU's behaviour when not actively receiving or + * transmitting on a given TDM slot. NONE is undefined behaviour. + * Add new modes to the end. + */ +#define SND_SOC_DAI_TDM_IDLE_NONE 0 +#define SND_SOC_DAI_TDM_IDLE_OFF 1 +#define SND_SOC_DAI_TDM_IDLE_ZERO 2 +#define SND_SOC_DAI_TDM_IDLE_PULLDOWN 3 +#define SND_SOC_DAI_TDM_IDLE_HIZ 4 +#define SND_SOC_DAI_TDM_IDLE_PULLUP 5 +#define SND_SOC_DAI_TDM_IDLE_DRIVE_HIGH 6 + +/* * DAI Clock gating. * * DAI bit clocks can be gated (disabled) when the DAI is not @@ -181,6 +196,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); +int snd_soc_dai_set_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode); + int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_slot, unsigned int rx_num, const unsigned int *rx_slot); @@ -297,6 +316,9 @@ struct snd_soc_dai_ops { int (*set_tdm_slot)(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); + int (*set_tdm_idle)(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode); int (*set_channel_map)(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_slot, unsigned int rx_num, const unsigned int *rx_slot); diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 49f0fe05db01..4f8fb7622a13 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -424,6 +424,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ snd_soc_dapm_output, /* output pin */ snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ + snd_soc_dapm_mux_named_ctl, /* mux with named controls */ snd_soc_dapm_demux, /* connects the input to one of multiple outputs */ snd_soc_dapm_mixer, /* mixes several analog signals together */ snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 7d8376c8e1be..5e3eb617d832 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -311,6 +311,12 @@ struct platform_device; .info = snd_soc_info_bool_ext, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = xdata } +#define SOC_SINGLE_BOOL_EXT_ACC(xname, xdata, xhandler_get, xhandler_put, xaccess) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = xaccess, \ + .info = snd_soc_info_bool_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = xdata } #define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ @@ -422,11 +428,6 @@ struct snd_soc_jack_pin; #include <sound/soc-dpcm.h> #include <sound/soc-topology.h> -enum snd_soc_pcm_subclass { - SND_SOC_PCM_CLASS_PCM = 0, - SND_SOC_PCM_CLASS_BE = 1, -}; - int snd_soc_register_card(struct snd_soc_card *card); void snd_soc_unregister_card(struct snd_soc_card *card); int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card); @@ -465,6 +466,7 @@ struct snd_soc_component *snd_soc_lookup_component_nolocked(struct device *dev, const char *driver_name); struct snd_soc_component *snd_soc_lookup_component(struct device *dev, const char *driver_name); +struct snd_soc_component *snd_soc_lookup_component_by_name(const char *component_name); int soc_new_pcm(struct snd_soc_pcm_runtime *rtd); #ifdef CONFIG_SND_SOC_COMPRESS @@ -999,7 +1001,6 @@ struct snd_soc_card { /* Mutex for PCM operations */ struct mutex pcm_mutex; - enum snd_soc_pcm_subclass pcm_subclass; int (*probe)(struct snd_soc_card *card); int (*late_probe)(struct snd_soc_card *card); @@ -1026,8 +1027,6 @@ struct snd_soc_card { void (*remove_dai_link)(struct snd_soc_card *, struct snd_soc_dai_link *link); - long pmdown_time; - /* CPU <--> Codec DAI links */ struct snd_soc_dai_link *dai_link; /* predefined links only */ int num_links; /* predefined links only */ @@ -1072,9 +1071,6 @@ struct snd_soc_card { struct list_head dapm_list; struct list_head dapm_dirty; - /* attached dynamic objects */ - struct list_head dobj_list; - /* Generic DAPM context for the card */ struct snd_soc_dapm_context *dapm; struct snd_soc_dapm_stats dapm_stats; @@ -1239,7 +1235,6 @@ struct soc_mixer_control { unsigned int sign_bit; unsigned int invert:1; unsigned int autodisable:1; - unsigned int sdca_q78:1; #ifdef CONFIG_SND_SOC_TOPOLOGY struct snd_soc_dobj dobj; #endif @@ -1340,15 +1335,6 @@ void snd_soc_of_parse_node_prefix(struct device_node *np, struct snd_soc_codec_conf *codec_conf, struct device_node *of_node, const char *propname); -static inline -void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, - struct snd_soc_codec_conf *codec_conf, - struct device_node *of_node, - const char *propname) -{ - snd_soc_of_parse_node_prefix(card->dev->of_node, - codec_conf, of_node, propname); -} int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); @@ -1412,6 +1398,9 @@ struct snd_soc_dai *snd_soc_find_dai( struct snd_soc_dai *snd_soc_find_dai_with_mutex( const struct snd_soc_dai_link_component *dlc); +void soc_pcm_set_dai_params(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params); + #include <sound/soc-dai.h> static inline @@ -1517,7 +1506,7 @@ static inline void _snd_soc_dapm_mutex_assert_held_d(struct snd_soc_dapm_context */ static inline void _snd_soc_dpcm_mutex_lock_c(struct snd_soc_card *card) { - mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass); + mutex_lock(&card->pcm_mutex); } static inline void _snd_soc_dpcm_mutex_unlock_c(struct snd_soc_card *card) diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 227347c8f0b3..489083183673 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -71,6 +71,7 @@ struct asoc_sdw_aux_info { }; struct asoc_sdw_codec_info { + const int vendor_id; const int part_id; const int version_id; const char *name_prefix; @@ -82,6 +83,8 @@ struct asoc_sdw_codec_info { const int dai_num; struct asoc_sdw_aux_info auxs[SOC_SDW_MAX_AUX_NUM]; const int aux_num; + /* Force AMP-style name_prefix handling (append AMP index) even if MIC/Jack DAIs exist */ + const bool is_amp; int (*codec_card_late_probe)(struct snd_soc_card *card); @@ -259,6 +262,8 @@ int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_so int asoc_sdw_cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); int asoc_sdw_cs42l45_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); int asoc_sdw_cs42l45_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); +int asoc_sdw_cs47l47_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); +int asoc_sdw_cs47l47_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); int asoc_sdw_maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); /* TI */ diff --git a/include/sound/tea6330t.h b/include/sound/tea6330t.h index 1c77b78f6533..3a34033d2aa3 100644 --- a/include/sound/tea6330t.h +++ b/include/sound/tea6330t.h @@ -12,5 +12,6 @@ int snd_tea6330t_detect(struct snd_i2c_bus *bus, int equalizer); int snd_tea6330t_update_mixer(struct snd_card *card, struct snd_i2c_bus *bus, int equalizer, int fader); +int snd_tea6330t_restore_mixer(struct snd_i2c_bus *bus); #endif /* __SOUND_TEA6330T_H */ diff --git a/include/sound/timer.h b/include/sound/timer.h index 760e132cc0cd..83bafe70cf33 100644 --- a/include/sound/timer.h +++ b/include/sound/timer.h @@ -102,6 +102,7 @@ struct snd_timer_instance { unsigned int slave_id; struct list_head open_list; struct list_head active_list; + struct list_head master_list; struct list_head ack_list; struct list_head slave_list_head; struct list_head slave_active_head; diff --git a/include/sound/uda1380.h b/include/sound/uda1380.h deleted file mode 100644 index 2e42ea2d0cfd..000000000000 --- a/include/sound/uda1380.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * UDA1380 ALSA SoC Codec driver - * - * Copyright 2009 Philipp Zabel - */ - -#ifndef __UDA1380_H -#define __UDA1380_H - -struct uda1380_platform_data { - int gpio_power; - int gpio_reset; - int dac_clk; -#define UDA1380_DAC_CLK_SYSCLK 0 -#define UDA1380_DAC_CLK_WSPLL 1 -}; - -#endif /* __UDA1380_H */ diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h index badff9f7cd54..b92593f170ca 100644 --- a/sound/aoa/aoa.h +++ b/sound/aoa/aoa.h @@ -48,7 +48,7 @@ struct aoa_codec { u32 connected; /* data the fabric can associate with this structure */ - void *fabric_data; + const void *fabric_data; /* private! */ struct list_head list; diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c index 04961c456d2c..4fb593e88fb1 100644 --- a/sound/aoa/codecs/onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -32,6 +32,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/slab.h> +#include <sound/asoundef.h> MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa"); @@ -514,8 +515,36 @@ static int onyx_spdif_put(struct snd_kcontrol *kcontrol, return 1; } +static int onyx_set_spdif_pcm_rate(struct onyx *onyx, unsigned int rate) +{ + u8 dig_info3, fs; + + switch (rate) { + case 32000: + fs = IEC958_AES3_CON_FS_32000; + break; + case 44100: + fs = IEC958_AES3_CON_FS_44100; + break; + case 48000: + fs = IEC958_AES3_CON_FS_48000; + break; + default: + return -EINVAL; + } + + if (onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &dig_info3)) + return -EBUSY; + dig_info3 = (dig_info3 & ~IEC958_AES3_CON_FS) | fs; + if (onyx_write_register(onyx, ONYX_REG_DIG_INFO3, dig_info3)) + return -EBUSY; + + return 0; +} + static const struct snd_kcontrol_new onyx_spdif_ctrl = { - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .info = onyx_spdif_info, @@ -695,9 +724,9 @@ static int onyx_prepare(struct codec_info_item *cii, case 32000: case 44100: case 48000: - /* these rates are ok for all outputs */ - /* FIXME: program spdif channel control bits here so that - * userspace doesn't have to if it only plays pcm! */ + if (onyx->codec.connected & 2) + return onyx_set_spdif_pcm_rate(onyx, + substream->runtime->rate); return 0; default: /* got some rate that the digital output can't do, @@ -980,10 +1009,12 @@ static int onyx_i2c_probe(struct i2c_client *client) onyx->codec.node = of_node_get(node); if (aoa_codec_register(&onyx->codec)) { - goto fail; + goto fail_put; } printk(KERN_DEBUG PFX "created and attached onyx instance\n"); return 0; + fail_put: + of_node_put(onyx->codec.node); fail: kfree(onyx); return -ENODEV; diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c index 13da2b159ad0..25214d3da65d 100644 --- a/sound/aoa/codecs/tas.c +++ b/sound/aoa/codecs/tas.c @@ -872,6 +872,7 @@ static int tas_i2c_probe(struct i2c_client *client) return 0; fail: mutex_destroy(&tas->mtx); + of_node_put(tas->codec.node); kfree(tas); return -EINVAL; } diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index c18b55305294..c3ebb6de4789 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -55,7 +55,7 @@ struct codec_connection { struct codec_connect_info { char *name; - struct codec_connection *connections; + const struct codec_connection *connections; }; #define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0) @@ -116,7 +116,7 @@ MODULE_ALIAS("aoa-device-id-35"); MODULE_ALIAS("aoa-device-id-44"); /* onyx with all but microphone connected */ -static struct codec_connection onyx_connections_nomic[] = { +static const struct codec_connection onyx_connections_nomic[] = { { .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, .codec_bit = 0, @@ -133,7 +133,7 @@ static struct codec_connection onyx_connections_nomic[] = { }; /* onyx on machines without headphone */ -static struct codec_connection onyx_connections_noheadphones[] = { +static const struct codec_connection onyx_connections_noheadphones[] = { { .connected = CC_SPEAKERS | CC_LINEOUT | CC_LINEOUT_LABELLED_HEADPHONE, @@ -157,7 +157,7 @@ static struct codec_connection onyx_connections_noheadphones[] = { }; /* onyx on machines with real line-out */ -static struct codec_connection onyx_connections_reallineout[] = { +static const struct codec_connection onyx_connections_reallineout[] = { { .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE, .codec_bit = 0, @@ -174,7 +174,7 @@ static struct codec_connection onyx_connections_reallineout[] = { }; /* tas on machines without line out */ -static struct codec_connection tas_connections_nolineout[] = { +static const struct codec_connection tas_connections_nolineout[] = { { .connected = CC_SPEAKERS | CC_HEADPHONE, .codec_bit = 0, @@ -191,7 +191,7 @@ static struct codec_connection tas_connections_nolineout[] = { }; /* tas on machines with neither line out nor line in */ -static struct codec_connection tas_connections_noline[] = { +static const struct codec_connection tas_connections_noline[] = { { .connected = CC_SPEAKERS | CC_HEADPHONE, .codec_bit = 0, @@ -204,7 +204,7 @@ static struct codec_connection tas_connections_noline[] = { }; /* tas on machines without microphone */ -static struct codec_connection tas_connections_nomic[] = { +static const struct codec_connection tas_connections_nomic[] = { { .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, .codec_bit = 0, @@ -217,7 +217,7 @@ static struct codec_connection tas_connections_nomic[] = { }; /* tas on machines with everything connected */ -static struct codec_connection tas_connections_all[] = { +static const struct codec_connection tas_connections_all[] = { { .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, .codec_bit = 0, @@ -233,7 +233,7 @@ static struct codec_connection tas_connections_all[] = { {} /* terminate array by .connected == 0 */ }; -static struct codec_connection toonie_connections[] = { +static const struct codec_connection toonie_connections[] = { { .connected = CC_SPEAKERS | CC_HEADPHONE, .codec_bit = 0, @@ -241,7 +241,7 @@ static struct codec_connection toonie_connections[] = { {} /* terminate array by .connected == 0 */ }; -static struct codec_connection topaz_input[] = { +static const struct codec_connection topaz_input[] = { { .connected = CC_DIGITALIN, .codec_bit = 0, @@ -249,7 +249,7 @@ static struct codec_connection topaz_input[] = { {} /* terminate array by .connected == 0 */ }; -static struct codec_connection topaz_output[] = { +static const struct codec_connection topaz_output[] = { { .connected = CC_DIGITALOUT, .codec_bit = 1, @@ -257,7 +257,7 @@ static struct codec_connection topaz_output[] = { {} /* terminate array by .connected == 0 */ }; -static struct codec_connection topaz_inout[] = { +static const struct codec_connection topaz_inout[] = { { .connected = CC_DIGITALIN, .codec_bit = 0, @@ -772,7 +772,7 @@ static int check_codec(struct aoa_codec *codec, { const u32 *ref; char propname[32]; - struct codec_connection *cc; + const struct codec_connection *cc; /* if the codec has a 'codec' node, we require a reference */ if (of_node_name_eq(codec->node, "codec")) { @@ -895,7 +895,7 @@ static void layout_notify(void *data) static void layout_attached_codec(struct aoa_codec *codec) { - struct codec_connection *cc; + const struct codec_connection *cc; struct snd_kcontrol *ctl; int headphones, lineout; struct layout_dev *ldev = layout_device; diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index f974b96e98cd..833c44c0a950 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -84,6 +84,7 @@ static void i2sbus_release_dev(struct device *dev) for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) free_irq(i2sdev->interrupts[i], i2sdev); i2sbus_control_remove_dev(i2sdev->control, i2sdev); + of_node_put(i2sdev->sound.ofdev.dev.of_node); mutex_destroy(&i2sdev->lock); kfree(i2sdev); } @@ -147,7 +148,6 @@ static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index, } /* Returns 1 if added, 0 for otherwise; don't return a negative value! */ -/* FIXME: look at device node refcounting */ static int i2sbus_add_dev(struct macio_dev *macio, struct i2sbus_control *control, struct device_node *np) @@ -178,8 +178,9 @@ static int i2sbus_add_dev(struct macio_dev *macio, i = 0; for_each_child_of_node(np, child) { if (of_node_name_eq(child, "sound")) { + of_node_put(sound); i++; - sound = child; + sound = of_node_get(child); } } if (i == 1) { @@ -205,6 +206,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, } } } + of_node_put(sound); /* for the time being, until we can handle non-layout-id * things in some fabric, refuse to attach if there is no * layout-id property or we haven't been forced to attach. @@ -219,7 +221,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, mutex_init(&dev->lock); spin_lock_init(&dev->low_lock); dev->sound.ofdev.archdata.dma_mask = macio->ofdev.archdata.dma_mask; - dev->sound.ofdev.dev.of_node = np; + dev->sound.ofdev.dev.of_node = of_node_get(np); dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.archdata.dma_mask; dev->sound.ofdev.dev.parent = &macio->ofdev.dev; dev->sound.ofdev.dev.release = i2sbus_release_dev; @@ -327,6 +329,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, for (i=0;i<3;i++) release_and_free_resource(dev->allocated_resource[i]); mutex_destroy(&dev->lock); + of_node_put(dev->sound.ofdev.dev.of_node); kfree(dev); return 0; } @@ -405,6 +408,9 @@ static int i2sbus_resume(struct macio_dev* dev) int err, ret = 0; list_for_each_entry(i2sdev, &control->list, item) { + if (list_empty(&i2sdev->sound.codec_list)) + continue; + /* reset i2s bus format etc. */ i2sbus_pcm_prepare_both(i2sdev); diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c index aff99003d833..63004ece94f9 100644 --- a/sound/aoa/soundbus/i2sbus/pcm.c +++ b/sound/aoa/soundbus/i2sbus/pcm.c @@ -165,17 +165,16 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) * currently in use (if any). */ hw->rate_min = 5512; hw->rate_max = 192000; - /* if the other stream is active, then we can only - * support what it is currently using. - * FIXME: I lied. This comment is wrong. We can support - * anything that works with the same serial format, ie. - * when recording 24 bit sound we can well play 16 bit - * sound at the same time iff using the same transfer mode. + /* If the other stream is already prepared, keep this stream + * on the same duplex format and rate. + * + * i2sbus_pcm_prepare() still programs one shared transport + * configuration for both directions, so mixed duplex formats + * are not supported here. */ if (other->active) { - /* FIXME: is this guaranteed by the alsa api? */ hw->formats &= pcm_format_to_bits(i2sdev->format); - /* see above, restrict rates to the one we already have */ + /* Restrict rates to the one already in use. */ hw->rate_min = i2sdev->rate; hw->rate_max = i2sdev->rate; } @@ -283,6 +282,23 @@ void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev) } #endif +static void i2sbus_pcm_clear_active(struct i2sbus_dev *i2sdev, int in) +{ + struct pcm_info *pi; + + guard(mutex)(&i2sdev->lock); + + get_pcm_info(i2sdev, in, &pi, NULL); + pi->active = 0; +} + +static inline int i2sbus_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, int in) +{ + i2sbus_pcm_clear_active(snd_pcm_substream_chip(substream), in); + return 0; +} + static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in) { struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); @@ -291,14 +307,27 @@ static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in) get_pcm_info(i2sdev, in, &pi, NULL); if (pi->dbdma_ring.stopping) i2sbus_wait_for_stop(i2sdev, pi); + i2sbus_pcm_clear_active(i2sdev, in); return 0; } +static int i2sbus_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return i2sbus_hw_params(substream, params, 0); +} + static int i2sbus_playback_hw_free(struct snd_pcm_substream *substream) { return i2sbus_hw_free(substream, 0); } +static int i2sbus_record_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return i2sbus_hw_params(substream, params, 1); +} + static int i2sbus_record_hw_free(struct snd_pcm_substream *substream) { return i2sbus_hw_free(substream, 1); @@ -335,7 +364,6 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) return -EINVAL; runtime = pi->substream->runtime; - pi->active = 1; if (other->active && ((i2sdev->format != runtime->format) || (i2sdev->rate != runtime->rate))) @@ -383,6 +411,9 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) /* set stop command */ command->command = cpu_to_le16(DBDMA_STOP); + cii = list_first_entry(&i2sdev->sound.codec_list, + struct codec_info_item, list); + /* ok, let's set the serial format and stuff */ switch (runtime->format) { /* 16 bit formats */ @@ -390,13 +421,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) case SNDRV_PCM_FORMAT_U16_BE: /* FIXME: if we add different bus factors we need to * do more here!! */ - bi.bus_factor = 0; - list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { - bi.bus_factor = cii->codec->bus_factor; - break; - } - if (!bi.bus_factor) - return -ENODEV; + bi.bus_factor = cii->codec->bus_factor; input_16bit = 1; break; case SNDRV_PCM_FORMAT_S32_BE: @@ -410,10 +435,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) return -EINVAL; } /* we assume all sysclocks are the same! */ - list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { - bi.sysclock_factor = cii->codec->sysclock_factor; - break; - } + bi.sysclock_factor = cii->codec->sysclock_factor; if (clock_and_divisors(bi.sysclock_factor, bi.bus_factor, @@ -450,9 +472,11 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) /* early exit if already programmed correctly */ /* not locking these is fine since we touch them only in this function */ - if (in_le32(&i2sdev->intfregs->serial_format) == sfr - && in_le32(&i2sdev->intfregs->data_word_sizes) == dws) + if (in_le32(&i2sdev->intfregs->serial_format) == sfr && + in_le32(&i2sdev->intfregs->data_word_sizes) == dws) { + pi->active = 1; return 0; + } /* let's notify the codecs about clocks going away. * For now we only do mastering on the i2s cell... */ @@ -490,6 +514,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) if (cii->codec->switch_clock) cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE); + pi->active = 1; return 0; } @@ -734,6 +759,7 @@ static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream static const struct snd_pcm_ops i2sbus_playback_ops = { .open = i2sbus_playback_open, .close = i2sbus_playback_close, + .hw_params = i2sbus_playback_hw_params, .hw_free = i2sbus_playback_hw_free, .prepare = i2sbus_playback_prepare, .trigger = i2sbus_playback_trigger, @@ -802,6 +828,7 @@ static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream static const struct snd_pcm_ops i2sbus_record_ops = { .open = i2sbus_record_open, .close = i2sbus_record_close, + .hw_params = i2sbus_record_hw_params, .hw_free = i2sbus_record_hw_free, .prepare = i2sbus_record_prepare, .trigger = i2sbus_record_trigger, diff --git a/sound/core/Makefile b/sound/core/Makefile index 31a0623cc89d..fdd3bb6e81a9 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -23,6 +23,7 @@ snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o # for trace-points CFLAGS_pcm_lib.o := -I$(src) CFLAGS_pcm_native.o := -I$(src) +CFLAGS_control.o := -I$(src) snd-pcm-dmaengine-y := pcm_dmaengine.o diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index fdba6e4b25fd..fd63d219bf86 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -41,13 +41,6 @@ #define COMPR_CODEC_CAPS_OVERFLOW #endif -/* TODO: - * - add substream support for multiple devices in case of - * SND_DYNAMIC_MINORS is not used - * - Multiple node representation - * driver should be able to register multiple nodes - */ - struct snd_compr_file { unsigned long caps; struct snd_compr_stream stream; @@ -190,9 +183,21 @@ snd_compr_tstamp32_from_64(struct snd_compr_tstamp *tstamp32, static int snd_compr_update_tstamp(struct snd_compr_stream *stream, struct snd_compr_tstamp64 *tstamp) { + int ret; + if (!stream->ops->pointer) return -ENOTSUPP; - stream->ops->pointer(stream, tstamp); + + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_OPEN: + return -EBADFD; + default: + break; + } + + ret = stream->ops->pointer(stream, tstamp); + if (ret != 0) + return ret; pr_debug("dsp consumed till %u total %llu bytes\n", tstamp->byte_offset, tstamp->copied_total); if (stream->direction == SND_COMPRESS_PLAYBACK) diff --git a/sound/core/control.c b/sound/core/control.c index 934e84e93838..5e51857635e6 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -19,6 +19,13 @@ #include <sound/info.h> #include <sound/control.h> +#ifdef CONFIG_SND_CTL_DEBUG +#define CREATE_TRACE_POINTS +#include "control_trace.h" +#else +#define trace_snd_ctl_put(card, kctl, iname, expected, actual) +#endif + // Max allocation size for user controls. static int max_user_ctl_alloc_size = 8 * 1024 * 1024; module_param_named(max_user_ctl_alloc_size, max_user_ctl_alloc_size, int, 0444); @@ -1264,6 +1271,72 @@ static int snd_ctl_elem_read_user(struct snd_card *card, return result; } +#if IS_ENABLED(CONFIG_SND_CTL_DEBUG) + +static const char *const snd_ctl_elem_iface_names[] = { + [SNDRV_CTL_ELEM_IFACE_CARD] = "CARD", + [SNDRV_CTL_ELEM_IFACE_HWDEP] = "HWDEP", + [SNDRV_CTL_ELEM_IFACE_MIXER] = "MIXER", + [SNDRV_CTL_ELEM_IFACE_PCM] = "PCM", + [SNDRV_CTL_ELEM_IFACE_RAWMIDI] = "RAWMIDI", + [SNDRV_CTL_ELEM_IFACE_TIMER] = "TIMER", + [SNDRV_CTL_ELEM_IFACE_SEQUENCER] = "SEQUENCER", +}; + +static int snd_ctl_put_verify(struct snd_card *card, struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *control) +{ + struct snd_ctl_elem_value *original = card->value_buf; + struct snd_ctl_elem_info info; + const char *iname; + int ret, retcmp; + + memset(original, 0, sizeof(*original)); + memset(&info, 0, sizeof(info)); + + ret = kctl->info(kctl, &info); + if (ret) + return ret; + + ret = kctl->get(kctl, original); + if (ret) + return ret; + + ret = kctl->put(kctl, control); + if (ret < 0) + return ret; + + /* Sanitize the new value (control->value) before comparing. */ + fill_remaining_elem_value(control, &info, 0); + + /* With known state for both new and original, do the comparison. */ + retcmp = memcmp(&original->value, &control->value, sizeof(original->value)); + if (retcmp) + retcmp = 1; + + iname = snd_ctl_elem_iface_names[kctl->id.iface]; + trace_snd_ctl_put(&kctl->id, iname, card->number, ret, retcmp); + + return ret; +} + +static int snd_ctl_put(struct snd_card *card, struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *control, unsigned int access) +{ + if ((access & SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK) || + (access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) + return kctl->put(kctl, control); + + return snd_ctl_put_verify(card, kctl, control); +} +#else +static inline int snd_ctl_put(struct snd_card *card, struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *control, unsigned int access) +{ + return kctl->put(kctl, control); +} +#endif + static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, struct snd_ctl_elem_value *control) { @@ -1300,7 +1373,8 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, false); } if (!result) - result = kctl->put(kctl, control); + result = snd_ctl_put(card, kctl, control, vd->access); + if (result < 0) { up_write(&card->controls_rwsem); return result; @@ -1574,6 +1648,10 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue) /* check that there are enough valid names */ p = names; for (i = 0; i < ue->info.value.enumerated.items; ++i) { + if (buf_len == 0) { + kvfree(names); + return -EINVAL; + } name_len = strnlen(p, buf_len); if (name_len == 0 || name_len >= 64 || name_len == buf_len) { kvfree(names); diff --git a/sound/core/control_trace.h b/sound/core/control_trace.h new file mode 100644 index 000000000000..d30e654b0860 --- /dev/null +++ b/sound/core/control_trace.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM snd_ctl + +#if !defined(_TRACE_SND_CTL_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SND_CTL_H + +#include <linux/tracepoint.h> +#include <uapi/sound/asound.h> + +TRACE_EVENT(snd_ctl_put, + + TP_PROTO(struct snd_ctl_elem_id *id, const char *iname, unsigned int card, + int expected, int actual), + + TP_ARGS(id, iname, card, expected, actual), + + TP_STRUCT__entry( + __field(unsigned int, numid) + __string(iname, iname) + __string(kname, id->name) + __field(unsigned int, index) + __field(unsigned int, device) + __field(unsigned int, subdevice) + __field(unsigned int, card) + __field(int, expected) + __field(int, actual) + ), + + TP_fast_assign( + __entry->numid = id->numid; + __assign_str(iname); + __assign_str(kname); + __entry->index = id->index; + __entry->device = id->device; + __entry->subdevice = id->subdevice; + __entry->card = card; + __entry->expected = expected; + __entry->actual = actual; + ), + + TP_printk("%s: expected=%d, actual=%d for ctl numid=%d, iface=%s, name='%s', index=%d, device=%d, subdevice=%d, card=%d\n", + __entry->expected == __entry->actual ? "success" : "fail", + __entry->expected, __entry->actual, __entry->numid, + __get_str(iname), __get_str(kname), __entry->index, + __entry->device, __entry->subdevice, __entry->card) +); + +#endif /* _TRACE_SND_CTL_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE control_trace +#include <trace/define_trace.h> diff --git a/sound/core/init.c b/sound/core/init.c index 2f1bd9cbdbed..0c316189e947 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -363,6 +363,11 @@ static int snd_card_init(struct snd_card *card, struct device *parent, card->debugfs_root = debugfs_create_dir(dev_name(&card->card_dev), sound_debugfs_root); #endif +#ifdef CONFIG_SND_CTL_DEBUG + card->value_buf = kmalloc(sizeof(*card->value_buf), GFP_KERNEL); + if (!card->value_buf) + return -ENOMEM; +#endif return 0; __error_ctl: @@ -587,6 +592,9 @@ static int snd_card_do_free(struct snd_card *card) snd_device_free_all(card); if (card->private_free) card->private_free(card); +#ifdef CONFIG_SND_CTL_DEBUG + kfree(card->value_buf); +#endif if (snd_info_card_free(card) < 0) { dev_warn(card->dev, "unable to free card info\n"); /* Not fatal error */ diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index d4fd4dfc7fc3..a140a0d9abb8 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -1227,14 +1227,16 @@ static int snd_pcm_oss_capture_position_fixup(struct snd_pcm_substream *substrea snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel) { struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_state_t state; int ret; while (1) { - if (runtime->state == SNDRV_PCM_STATE_XRUN || - runtime->state == SNDRV_PCM_STATE_SUSPENDED) { + state = snd_pcm_get_state(substream); + if (state == SNDRV_PCM_STATE_XRUN || + state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG pcm_dbg(substream->pcm, "pcm_oss: write: recovering from %s\n", - runtime->state == SNDRV_PCM_STATE_XRUN ? + state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif ret = snd_pcm_oss_prepare(substream); @@ -1249,7 +1251,7 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const break; /* test, if we can't store new data, because the stream */ /* has not been started */ - if (runtime->state == SNDRV_PCM_STATE_PREPARED) + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_PREPARED) return -EAGAIN; } return ret; @@ -1259,20 +1261,22 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t delay; + snd_pcm_state_t state; int ret; while (1) { - if (runtime->state == SNDRV_PCM_STATE_XRUN || - runtime->state == SNDRV_PCM_STATE_SUSPENDED) { + state = snd_pcm_get_state(substream); + if (state == SNDRV_PCM_STATE_XRUN || + state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG pcm_dbg(substream->pcm, "pcm_oss: read: recovering from %s\n", - runtime->state == SNDRV_PCM_STATE_XRUN ? + state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); if (ret < 0) break; - } else if (runtime->state == SNDRV_PCM_STATE_SETUP) { + } else if (state == SNDRV_PCM_STATE_SETUP) { ret = snd_pcm_oss_prepare(substream); if (ret < 0) break; @@ -1285,7 +1289,7 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p frames, in_kernel); mutex_lock(&runtime->oss.params_lock); if (ret == -EPIPE) { - if (runtime->state == SNDRV_PCM_STATE_DRAINING) { + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_DRAINING) { ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); if (ret < 0) break; @@ -1301,15 +1305,16 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p #ifdef CONFIG_SND_PCM_OSS_PLUGINS snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames) { - struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_state_t state; int ret; while (1) { - if (runtime->state == SNDRV_PCM_STATE_XRUN || - runtime->state == SNDRV_PCM_STATE_SUSPENDED) { + state = snd_pcm_get_state(substream); + if (state == SNDRV_PCM_STATE_XRUN || + state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG pcm_dbg(substream->pcm, "pcm_oss: writev: recovering from %s\n", - runtime->state == SNDRV_PCM_STATE_XRUN ? + state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif ret = snd_pcm_oss_prepare(substream); @@ -1322,7 +1327,7 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void /* test, if we can't store new data, because the stream */ /* has not been started */ - if (runtime->state == SNDRV_PCM_STATE_PREPARED) + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_PREPARED) return -EAGAIN; } return ret; @@ -1330,21 +1335,22 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames) { - struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_state_t state; int ret; while (1) { - if (runtime->state == SNDRV_PCM_STATE_XRUN || - runtime->state == SNDRV_PCM_STATE_SUSPENDED) { + state = snd_pcm_get_state(substream); + if (state == SNDRV_PCM_STATE_XRUN || + state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG pcm_dbg(substream->pcm, "pcm_oss: readv: recovering from %s\n", - runtime->state == SNDRV_PCM_STATE_XRUN ? + state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); if (ret < 0) break; - } else if (runtime->state == SNDRV_PCM_STATE_SETUP) { + } else if (state == SNDRV_PCM_STATE_SETUP) { ret = snd_pcm_oss_prepare(substream); if (ret < 0) break; diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index e71f393d3b01..5313f50f17da 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -430,11 +430,13 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, if (!boundary) boundary = 0x7fffffff; scoped_guard(pcm_stream_lock_irq, substream) { - /* FIXME: we should consider the boundary for the sync from app */ - if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) - control->appl_ptr = scontrol.appl_ptr; - else + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) { + err = pcm_lib_apply_appl_ptr(substream, scontrol.appl_ptr); + if (err < 0) + return err; + } else { scontrol.appl_ptr = control->appl_ptr % boundary; + } if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) control->avail_min = scontrol.avail_min; else diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 5a64453da728..a541bb235cfa 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -618,13 +618,32 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime) return usecs; } -static void snd_pcm_set_state(struct snd_pcm_substream *substream, - snd_pcm_state_t state) +/** + * snd_pcm_set_state - Set the PCM runtime state with stream lock + * @substream: PCM substream + * @state: state to set + */ +void snd_pcm_set_state(struct snd_pcm_substream *substream, + snd_pcm_state_t state) { guard(pcm_stream_lock_irq)(substream); if (substream->runtime->state != SNDRV_PCM_STATE_DISCONNECTED) __snd_pcm_set_state(substream->runtime, state); } +EXPORT_SYMBOL_GPL(snd_pcm_set_state); + +/** + * snd_pcm_get_state - Read the PCM runtime state with stream lock + * @substream: PCM substream + * + * Return: the current PCM state + */ +snd_pcm_state_t snd_pcm_get_state(struct snd_pcm_substream *substream) +{ + guard(pcm_stream_lock_irqsave)(substream); + return substream->runtime->state; +} +EXPORT_SYMBOL_GPL(snd_pcm_get_state); static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream, int event) @@ -1761,6 +1780,9 @@ static int snd_pcm_suspend(struct snd_pcm_substream *substream) * snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm * @pcm: the PCM instance * + * Takes and releases pcm->open_mutex to serialize against + * concurrent open/close while walking the substreams. + * * After this call, all streams are changed to SUSPENDED state. * * Return: Zero if successful (or @pcm is %NULL), or a negative error code. @@ -1773,8 +1795,9 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm) if (! pcm) return 0; + guard(mutex)(&pcm->open_mutex); + for_each_pcm_substream(pcm, stream, substream) { - /* FIXME: the open/close code should lock this as well */ if (!substream->runtime) continue; diff --git a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c index 8a142fd54a19..307ef98c44c7 100644 --- a/sound/core/seq/oss/seq_oss_rw.c +++ b/sound/core/seq/oss/seq_oss_rw.c @@ -101,9 +101,9 @@ snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count, break; } fmt = (*(unsigned short *)rec.c) & 0xffff; - /* FIXME the return value isn't correct */ - return snd_seq_oss_synth_load_patch(dp, rec.s.dev, - fmt, buf, 0, count); + err = snd_seq_oss_synth_load_patch(dp, rec.s.dev, + fmt, buf, 0, count); + return err < 0 ? err : count; } if (ev_is_long(&rec)) { /* extended code */ diff --git a/sound/core/seq/seq_ump_convert.c b/sound/core/seq/seq_ump_convert.c index db2f169cae11..ff4ee26adad1 100644 --- a/sound/core/seq/seq_ump_convert.c +++ b/sound/core/seq/seq_ump_convert.c @@ -841,7 +841,7 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event, unsigned char index = event->data.control.param & 0x7f; unsigned char val = event->data.control.value & 0x7f; struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel]; - int ret; + int ret = 0; /* process special CC's (bank/rpn/nrpn) */ switch (index) { @@ -851,47 +851,54 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event, cc->cc_rpn_msb = val; if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f) reset_rpn(cc); - return ret; + break; case UMP_CC_RPN_LSB: ret = fill_rpn(cc, data, channel, true); cc->rpn_set = 1; cc->cc_rpn_lsb = val; if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f) reset_rpn(cc); - return ret; + break; case UMP_CC_NRPN_MSB: ret = fill_rpn(cc, data, channel, true); cc->nrpn_set = 1; cc->cc_nrpn_msb = val; - return ret; + break; case UMP_CC_NRPN_LSB: ret = fill_rpn(cc, data, channel, true); cc->nrpn_set = 1; cc->cc_nrpn_lsb = val; - return ret; + break; case UMP_CC_DATA: cc->cc_data_msb_set = 1; cc->cc_data_msb = val; - return fill_rpn(cc, data, channel, false); + ret = fill_rpn(cc, data, channel, false); + break; case UMP_CC_BANK_SELECT: cc->bank_set = 1; cc->cc_bank_msb = val; - return 0; // skip + ret = 0; // skip + break; case UMP_CC_BANK_SELECT_LSB: cc->bank_set = 1; cc->cc_bank_lsb = val; - return 0; // skip + ret = 0; // skip + break; case UMP_CC_DATA_LSB: cc->cc_data_lsb_set = 1; cc->cc_data_lsb = val; - return fill_rpn(cc, data, channel, false); + ret = fill_rpn(cc, data, channel, false); + break; + default: + data->cc.status = status; + data->cc.channel = channel; + data->cc.index = index; + data->cc.data = upscale_7_to_32bit(event->data.control.value & 0x7f); + ret = 1; + break; } - data->cc.status = status; - data->cc.channel = channel; - data->cc.index = index; - data->cc.data = upscale_7_to_32bit(event->data.control.value & 0x7f); - return 1; + return ret; } /* convert one-parameter control event to MIDI 2.0 UMP */ diff --git a/sound/core/sound.c b/sound/core/sound.c index 93436db24710..8d05fe0d263b 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -216,9 +216,16 @@ static int snd_find_free_minor(int type, struct snd_card *card, int dev) case SNDRV_DEVICE_TYPE_RAWMIDI: case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: case SNDRV_DEVICE_TYPE_PCM_CAPTURE: + if (snd_BUG_ON(!card)) + return -EINVAL; + minor = SNDRV_MINOR(card->number, type + dev); + break; case SNDRV_DEVICE_TYPE_COMPRESS: if (snd_BUG_ON(!card)) return -EINVAL; + if (dev < 0 || + dev >= SNDRV_MINOR_HWDEP - SNDRV_MINOR_COMPRESS) + return -EINVAL; minor = SNDRV_MINOR(card->number, type + dev); break; default: diff --git a/sound/core/timer.c b/sound/core/timer.c index 6a70df7ae019..820901d503af 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -129,6 +129,9 @@ static LIST_HEAD(snd_timer_list); /* list of slave instances */ static LIST_HEAD(snd_timer_slave_list); +/* list of open master instances that can accept slave links */ +static LIST_HEAD(snd_timer_master_list); + /* lock for slave active lists */ static DEFINE_SPINLOCK(slave_active_lock); @@ -161,6 +164,7 @@ struct snd_timer_instance *snd_timer_instance_new(const char *owner) } INIT_LIST_HEAD(&timeri->open_list); INIT_LIST_HEAD(&timeri->active_list); + INIT_LIST_HEAD(&timeri->master_list); INIT_LIST_HEAD(&timeri->ack_list); INIT_LIST_HEAD(&timeri->slave_list_head); INIT_LIST_HEAD(&timeri->slave_active_head); @@ -245,6 +249,12 @@ static int check_matching_master_slave(struct snd_timer_instance *master, return 1; } +static bool snd_timer_has_slave_key(const struct snd_timer_instance *timeri) +{ + return !(timeri->flags & SNDRV_TIMER_IFLG_SLAVE) && + timeri->slave_class > SNDRV_TIMER_SCLASS_NONE; +} + /* * look for a master instance matching with the slave id of the given slave. * when found, relink the open_link of the slave. @@ -253,19 +263,15 @@ static int check_matching_master_slave(struct snd_timer_instance *master, */ static int snd_timer_check_slave(struct snd_timer_instance *slave) { - struct snd_timer *timer; struct snd_timer_instance *master; int err = 0; - /* FIXME: it's really dumb to look up all entries.. */ - list_for_each_entry(timer, &snd_timer_list, device_list) { - list_for_each_entry(master, &timer->open_list_head, open_list) { - err = check_matching_master_slave(master, slave); - if (err != 0) /* match found or error */ - goto out; - } + list_for_each_entry(master, &snd_timer_master_list, master_list) { + err = check_matching_master_slave(master, slave); + if (err != 0) /* match found or error */ + goto out; } - out: +out: return err < 0 ? err : 0; } @@ -377,6 +383,8 @@ int snd_timer_open(struct snd_timer_instance *timeri, timeri->slave_id = slave_id; list_add_tail(&timeri->open_list, &timer->open_list_head); + if (snd_timer_has_slave_key(timeri)) + list_add_tail(&timeri->master_list, &snd_timer_master_list); timer->num_instances++; err = snd_timer_check_master(timeri); list_added: @@ -431,6 +439,9 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri, num_slaves--; } + if (!list_empty(&timeri->master_list)) + list_del_init(&timeri->master_list); + /* force to stop the timer */ snd_timer_stop(timeri); diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c index 2b595ee0bc35..05550f36fac5 100644 --- a/sound/firewire/fireworks/fireworks_command.c +++ b/sound/firewire/fireworks/fireworks_command.c @@ -151,10 +151,13 @@ efw_transaction(struct snd_efw *efw, unsigned int category, (be32_to_cpu(header->category) != category) || (be32_to_cpu(header->command) != command) || (be32_to_cpu(header->status) != EFR_STATUS_OK)) { + u32 st = be32_to_cpu(header->status); + dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n", be32_to_cpu(header->category), be32_to_cpu(header->command), - efr_status_names[be32_to_cpu(header->status)]); + st < ARRAY_SIZE(efr_status_names) ? + efr_status_names[st] : "unknown"); err = -EIO; goto end; } diff --git a/sound/hda/codecs/analog.c b/sound/hda/codecs/analog.c index 11b1d30b23fd..1ba8ae54e25e 100644 --- a/sound/hda/codecs/analog.c +++ b/sound/hda/codecs/analog.c @@ -38,6 +38,8 @@ struct ad198x_spec { unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ int num_smux_conns; + + unsigned int gpio_data; }; @@ -934,9 +936,9 @@ static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled) if (spec->eapd_nid) ad_vmaster_eapd_hook(private_data, enabled); - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, - enabled ? 0x00 : 0x02); + spec->gpio_data = enabled ? 0x00 : 0x02; + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, spec->gpio_data); } static void ad1884_fixup_hp_eapd(struct hda_codec *codec, @@ -948,12 +950,7 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec, case HDA_FIXUP_ACT_PRE_PROBE: spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; spec->gen.own_eapd_ctl = 1; - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, 0x02); - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x02); - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x02); + spec->gpio_data = 0x02; break; case HDA_FIXUP_ACT_PROBE: if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -961,6 +958,9 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec, else spec->eapd_nid = spec->gen.autocfg.speaker_pins[0]; break; + case HDA_FIXUP_ACT_INIT: + snd_hda_codec_set_gpio(codec, 0x02, 0x02, spec->gpio_data, 0); + break; } } diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c index a0677d7da8e2..ad533b04ab29 100644 --- a/sound/hda/codecs/ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -3755,22 +3755,12 @@ static void ca0132_gpio_setup(struct hda_codec *codec) switch (ca0132_quirk(spec)) { case QUIRK_SBZ: - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x07); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, 0x07); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x04); + snd_hda_codec_set_gpio(codec, 0x07, 0x07, 0x04, 0); snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x06); break; case QUIRK_R3DI: - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x1E); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, 0x1F); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x0C); + snd_hda_codec_set_gpio(codec, 0x1F, 0x1E, 0x0C, 0); break; default: break; diff --git a/sound/hda/codecs/cirrus/cs420x.c b/sound/hda/codecs/cirrus/cs420x.c index 52eda0a338ea..42559edbba05 100644 --- a/sound/hda/codecs/cirrus/cs420x.c +++ b/sound/hda/codecs/cirrus/cs420x.c @@ -264,14 +264,9 @@ static int cs_init(struct hda_codec *codec) snd_hda_gen_init(codec); - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } + if (spec->gpio_mask) + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, 0); if (spec->vendor_nid == CS420X_VENDOR_NID) { init_input_coef(codec); diff --git a/sound/hda/codecs/cirrus/cs421x.c b/sound/hda/codecs/cirrus/cs421x.c index c8349a2c5a36..645b06599e5f 100644 --- a/sound/hda/codecs/cirrus/cs421x.c +++ b/sound/hda/codecs/cirrus/cs421x.c @@ -442,14 +442,9 @@ static int cs421x_init(struct hda_codec *codec) snd_hda_gen_init(codec); - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } + if (spec->gpio_mask) + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, 0); cs4210_spdif_automute(codec, NULL); diff --git a/sound/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c index fad705092777..c43ff3ef75b6 100644 --- a/sound/hda/codecs/cirrus/cs8409.c +++ b/sound/hda/codecs/cirrus/cs8409.c @@ -268,7 +268,7 @@ static int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_para return 0; error: - codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr); + codec_err(codec, "I2C Bulk Read Failed 0x%02x\n", scodec->addr); return -EIO; } @@ -1042,14 +1042,9 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec) struct cs8409_spec *spec = codec->spec; struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; - if (spec->gpio_mask) { - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } + if (spec->gpio_mask) + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, 0); for (; seq->nid; seq++) cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); @@ -1442,14 +1437,9 @@ static void dolphin_hw_init(struct hda_codec *codec) struct sub_codec *cs42l42; int i; - if (spec->gpio_mask) { - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } + if (spec->gpio_mask) + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, 0); for (; seq->nid; seq++) cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); diff --git a/sound/hda/codecs/cmedia.c b/sound/hda/codecs/cmedia.c index e6e12c01339f..88dd80d987d4 100644 --- a/sound/hda/codecs/cmedia.c +++ b/sound/hda/codecs/cmedia.c @@ -46,13 +46,6 @@ static int cmedia_probe(struct hda_codec *codec, const struct hda_device_id *id) if (err < 0) goto error; - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - goto error; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - goto error; - if (is_cmi8888) { if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) == AC_JACK_HP_OUT) { diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index aa726eb323eb..3a9717df39b4 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -154,14 +154,8 @@ static void cxt_init_gpio_led(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; - if (mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); - } + if (mask) + snd_hda_codec_set_gpio(codec, mask, mask, spec->gpio_led, 0); } static void cx_fixup_headset_recog(struct hda_codec *codec) @@ -775,9 +769,7 @@ static void cxt_setup_gpio_unmute(struct hda_codec *codec, { if (gpio_mute_mask) { // set gpio data to 0. - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, gpio_mute_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, gpio_mute_mask); + snd_hda_codec_set_gpio(codec, gpio_mute_mask, gpio_mute_mask, 0, 0); snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_STICKY_MASK, 0); } } diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 092428ada29d..660a9f2c0ded 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -863,7 +863,7 @@ static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid) { if (nid) { msleep(10); - snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + snd_hda_codec_write_sync(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); } } diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c index 9460c8db39a9..6a7882544ab7 100644 --- a/sound/hda/codecs/hdmi/intelhdmi.c +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -85,7 +85,7 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, } } - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); + snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); snd_hda_codec_set_power_to_all(codec, fg, power_state); } diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 45f9d6487388..b33f425763f9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1004,13 +1004,6 @@ static int alc269_resume(struct hda_codec *codec) snd_hda_regmap_sync(codec); hda_call_check_power_status(codec, 0x01); - /* on some machine, the BIOS will clear the codec gpio data when enter - * suspend, and won't restore the data after resume, so we restore it - * in the driver. - */ - if (spec->gpio_data) - alc_write_gpio_data(codec); - if (spec->has_alc5505_dsp) alc5505_dsp_resume(codec); @@ -2296,9 +2289,9 @@ static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec, struct alc_spec *spec = codec->spec; spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; alc255_set_default_jack_type(codec); - } - else + } else { alc_fixup_headset_mode(codec, fix, action); + } } static void alc288_update_headset_jack_cb(struct hda_codec *codec, @@ -3661,22 +3654,11 @@ static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream, int action) { - static const struct coef_fw dis_coefs[] = { - WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC203), - WRITE_COEF(0x28, 0x0004), WRITE_COEF(0x29, 0xb023), - }; /* Disable AMP silence detection */ - static const struct coef_fw en_coefs[] = { - WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC203), - WRITE_COEF(0x28, 0x0084), WRITE_COEF(0x29, 0xb023), - }; /* Enable AMP silence detection */ - switch (action) { case HDA_GEN_PCM_ACT_OPEN: - alc_process_coef_fw(codec, dis_coefs); alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */ break; case HDA_GEN_PCM_ACT_CLOSE: - alc_process_coef_fw(codec, en_coefs); alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ break; } @@ -3699,10 +3681,15 @@ static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC301), WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), }; + static const struct coef_fw dis_coefs[] = { + WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC203), + WRITE_COEF(0x28, 0x0004), WRITE_COEF(0x29, 0xb023), + }; /* Disable AMP silence detection */ if (action != HDA_FIXUP_ACT_PRE_PROBE) return; alc_update_coef_idx(codec, 0x10, 1<<11, 1<<11); + alc_process_coef_fw(codec, dis_coefs); alc_process_coef_fw(codec, coefs); spec->power_hook = alc287_s4_power_gpio3_default; spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; @@ -6639,10 +6626,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc288_fixup_surface_swap_dacs, }, - [ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_fixup_lenovo_gpio2_mic_hotkey, - }, + [ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_fixup_lenovo_gpio2_mic_hotkey, + }, [ALC245_FIXUP_BASS_HP_DAC] = { .type = HDA_FIXUP_FUNC, /* Borrow the DAC routing selected for those Thinkpads */ @@ -6718,6 +6705,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED), @@ -6954,6 +6942,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8847, "HP EliteBook x360 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x884b, "HP EliteBook 840 Aero G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x884c, "HP EliteBook 840 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), + HDA_CODEC_QUIRK(0x103c, 0x885b, "HP Spectre x360 14-ea", ALC245_FIXUP_HP_X360_AMP), SND_PCI_QUIRK(0x103c, 0x8862, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x8863, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x886d, "HP ZBook Fury 17.3 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), @@ -7198,6 +7187,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e75, "HP Trekker G7JC", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e8a, "HP NexusX", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8e9c, "HP 16 Clipper OmniBook X X360", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), @@ -7219,8 +7209,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8ee4, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), SND_PCI_QUIRK(0x103c, 0x8ee5, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), SND_PCI_QUIRK(0x103c, 0x8ee7, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8f07, "HP Agusta G7KX", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f0c, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8f0e, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8f2d, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8f2e, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f40, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f41, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), @@ -7365,6 +7358,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x3391, "ASUS PM3606CKA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3601, "ASUS PM5406CGA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3611, "ASUS PM5606CGA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3701, "ASUS P5406CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3711, "ASUS P5606CCA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), @@ -7605,6 +7602,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3811, "Legion S7 15IMH05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7), SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c index 5073165d1f3c..2cf3664a35ff 100644 --- a/sound/hda/codecs/realtek/alc662.c +++ b/sound/hda/codecs/realtek/alc662.c @@ -255,6 +255,17 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, alc_fixup_headset_mode(codec, fix, action); } +static void alc662_fixup_csl_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_INIT) { + /* need to toggle GPIO to enable the amp */ + snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x03, 0); + msleep(100); + snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x00, 0); + } +} + enum { ALC662_FIXUP_ASPIRE, ALC662_FIXUP_LED_GPIO1, @@ -313,6 +324,7 @@ enum { ALC897_FIXUP_HEADSET_MIC_PIN2, ALC897_FIXUP_UNIS_H3C_X500S, ALC897_FIXUP_HEADSET_MIC_PIN3, + ALC662_FIXUP_CSL_GPIO, }; static const struct hda_fixup alc662_fixups[] = { @@ -766,11 +778,16 @@ static const struct hda_fixup alc662_fixups[] = { { } }, }, + [ALC662_FIXUP_CSL_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_csl_amp, + }, }; static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3), + SND_PCI_QUIRK(0x1022, 0xc950, "CSL Unity BF24B", ALC662_FIXUP_CSL_GPIO), SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c index aad265c0a5b6..db365a746b1a 100644 --- a/sound/hda/codecs/realtek/realtek.c +++ b/sound/hda/codecs/realtek/realtek.c @@ -99,15 +99,6 @@ void alc_setup_gpio(struct hda_codec *codec, unsigned int mask) } EXPORT_SYMBOL_NS_GPL(alc_setup_gpio, "SND_HDA_CODEC_REALTEK"); -void alc_write_gpio_data(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); -} -EXPORT_SYMBOL_NS_GPL(alc_write_gpio_data, "SND_HDA_CODEC_REALTEK"); - void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, bool on) { @@ -119,26 +110,22 @@ void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, else spec->gpio_data &= ~mask; if (oldval != spec->gpio_data) - alc_write_gpio_data(codec); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); } EXPORT_SYMBOL_NS_GPL(alc_update_gpio_data, "SND_HDA_CODEC_REALTEK"); -void alc_write_gpio(struct hda_codec *codec) +static void alc_write_gpio(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; if (!spec->gpio_mask) return; - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_MASK, spec->gpio_mask); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir); - if (spec->gpio_write_delay) - msleep(1); - alc_write_gpio_data(codec); + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, + spec->gpio_write_delay ? 1 : 0); } -EXPORT_SYMBOL_NS_GPL(alc_write_gpio, "SND_HDA_CODEC_REALTEK"); void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask) { @@ -411,9 +398,8 @@ void alc_headset_mic_no_shutup(struct hda_codec *codec) return; snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ if (pin->nid != mic_pin) - snd_hda_codec_read(codec, pin->nid, 0, + snd_hda_codec_write_sync(codec, pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); } @@ -2164,8 +2150,7 @@ void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { struct alc_spec *spec = codec->spec; spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - } - else + } else alc_fixup_headset_mode(codec, fix, action); } EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode_no_hp_mic, "SND_HDA_CODEC_REALTEK"); diff --git a/sound/hda/codecs/realtek/realtek.h b/sound/hda/codecs/realtek/realtek.h index b2a919904c4c..de95642bb648 100644 --- a/sound/hda/codecs/realtek/realtek.h +++ b/sound/hda/codecs/realtek/realtek.h @@ -168,10 +168,8 @@ void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw); * GPIO helpers */ void alc_setup_gpio(struct hda_codec *codec, unsigned int mask); -void alc_write_gpio_data(struct hda_codec *codec); void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, bool on); -void alc_write_gpio(struct hda_codec *codec); /* common GPIO fixups */ void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask); diff --git a/sound/hda/codecs/senarytech.c b/sound/hda/codecs/senarytech.c index 6239a25bb8f3..3ee8bc0ea3ab 100644 --- a/sound/hda/codecs/senarytech.c +++ b/sound/hda/codecs/senarytech.c @@ -36,6 +36,32 @@ struct senary_spec { unsigned int gpio_mic_led_mask; }; +enum { + SENARY_FIXUP_PINCFG_DEFAULT, +}; + +static const struct hda_pintbl senary_pincfg_default[] = { + { 0x16, 0x02211020 }, /* Headphone */ + { 0x17, 0x40f001f0 }, /* Not used */ + { 0x18, 0x05a1904d }, /* Mic */ + { 0x19, 0x02a1104e }, /* Headset Mic */ + { 0x1a, 0x01819030 }, /* Line-in */ + { 0x1d, 0x01014010 }, /* Line-out */ + {} +}; + +static const struct hda_fixup senary_fixups[] = { + [SENARY_FIXUP_PINCFG_DEFAULT] = { + .type = HDA_FIXUP_PINS, + .v.pins = senary_pincfg_default, + }, +}; + +/* Quirk table for specific machines can be added here */ +static const struct hda_quirk sn6186_fixups[] = { + {} +}; + #ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; private_value will be overwritten */ static const struct snd_kcontrol_new senary_beep_mixer[] = { @@ -50,7 +76,6 @@ static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid, unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); int i; - spec->gen.beep_nid = nid; for (i = 0; i < ARRAY_SIZE(senary_beep_mixer); i++) { knew = snd_hda_gen_add_kctl(&spec->gen, NULL, &senary_beep_mixer[i]); @@ -58,6 +83,8 @@ static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid, return -ENOMEM; knew->private_value = beep_amp; } + + spec->gen.beep_nid = nid; return 0; } @@ -93,16 +120,28 @@ static void senary_auto_parse_eapd(struct hda_codec *codec) } } +/* Hardware specific initialization verbs */ +static void senary_init_verb(struct hda_codec *codec) +{ + /* Vendor specific init sequence */ + snd_hda_codec_write(codec, 0x1b, 0x0, 0x05a, 0xaa); + snd_hda_codec_write(codec, 0x1b, 0x0, 0x059, 0x48); + snd_hda_codec_write(codec, 0x1b, 0x0, 0x01b, 0x00); + snd_hda_codec_write(codec, 0x1b, 0x0, 0x01c, 0x00); + + /* Override pin caps for headset mic */ + snd_hda_override_pin_caps(codec, 0x19, 0x2124); +} + static void senary_auto_turn_eapd(struct hda_codec *codec, int num_pins, const hda_nid_t *pins, bool on) { int i; for (i = 0; i < num_pins; i++) { - if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_EAPD_BTLENABLE, - on ? 0x02 : 0); + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_EAPD_BTLENABLE, + on ? 0x02 : 0); } } @@ -120,14 +159,8 @@ static void senary_init_gpio_led(struct hda_codec *codec) struct senary_spec *spec = codec->spec; unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; - if (mask) { - snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, - mask); - snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, - mask); - snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); - } + if (mask) + snd_hda_codec_set_gpio(codec, mask, mask, spec->gpio_led, 0); } static int senary_init(struct hda_codec *codec) @@ -136,6 +169,7 @@ static int senary_init(struct hda_codec *codec) snd_hda_gen_init(codec); senary_init_gpio_led(codec); + senary_init_verb(codec); if (!spec->dynamic_eapd) senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); @@ -181,11 +215,30 @@ static int senary_probe(struct hda_codec *codec, const struct hda_device_id *id) senary_auto_parse_eapd(codec); spec->gen.own_eapd_ctl = 1; - if (!spec->gen.vmaster_mute.hook) - spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook; + /* Setup fixups based on codec vendor ID */ + switch (codec->core.vendor_id) { + case 0x1fa86186: + codec->pin_amp_workaround = 1; + spec->gen.mixer_nid = 0x15; + snd_hda_pick_fixup(codec, NULL, sn6186_fixups, senary_fixups); + + /* If no specific quirk found, apply the default pin configuration */ + if (codec->fixup_id == HDA_FIXUP_ID_NOT_SET) + codec->fixup_id = SENARY_FIXUP_PINCFG_DEFAULT; + break; + default: + snd_hda_pick_fixup(codec, NULL, sn6186_fixups, senary_fixups); + break; + } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + /* Run hardware init verbs once during probe */ + senary_init_verb(codec); + + if (!spec->gen.vmaster_mute.hook) + spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook; + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, spec->parse_flags); if (err < 0) diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig index f674e9a9c7d7..fc5651e555e3 100644 --- a/sound/hda/codecs/side-codecs/Kconfig +++ b/sound/hda/codecs/side-codecs/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config SND_HDA_CIRRUS_SCODEC tristate diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_property.c b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c index 16d5ea77192f..732ae534db36 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda_property.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c @@ -55,6 +55,11 @@ static const struct cs35l41_config cs35l41_config_table[] = { { "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8A6E", 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 }, +/* + * Device 103C8B63 has _DSD with valid reset-gpios and cs-gpios, however the + * boost type is incorrectly set to Internal. Override to External Boost. + */ + { "103C8B63", 4, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, -1, -1, -1, 0, 0, 0 }, { "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, @@ -475,6 +480,7 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { { "CSC3551", "103C8A30", generic_dsd_config }, { "CSC3551", "103C8A31", generic_dsd_config }, { "CSC3551", "103C8A6E", generic_dsd_config }, + { "CSC3551", "103C8B63", generic_dsd_config }, { "CSC3551", "103C8BB3", generic_dsd_config }, { "CSC3551", "103C8BB4", generic_dsd_config }, { "CSC3551", "103C8BDD", generic_dsd_config }, diff --git a/sound/hda/codecs/sigmatel.c b/sound/hda/codecs/sigmatel.c index acbbc7c3508b..ee3bd21adc36 100644 --- a/sound/hda/codecs/sigmatel.c +++ b/sound/hda/codecs/sigmatel.c @@ -309,15 +309,7 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, /* Configure GPIOx as CMOS */ snd_hda_codec_write(codec, fg, 0, 0x7e7, 0); - snd_hda_codec_write(codec, fg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, fg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ - - msleep(1); - - snd_hda_codec_read(codec, fg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ + snd_hda_codec_set_gpio(codec, gpiomask, gpiodir, gpiostate, 1); } /* hook for controlling mic-mute LED GPIO */ diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 09b1329bb8f3..c2af2511a831 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -606,9 +606,8 @@ void snd_hda_shutup_pins(struct hda_codec *codec) if (codec->bus->shutdown) return; snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ - snd_hda_codec_read(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write_sync(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); } codec->pins_shutup = 1; } @@ -2529,7 +2528,10 @@ EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign); static int spdif_share_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_multi_out *mout = (void *)kcontrol->private_value; + + guard(mutex)(&codec->spdif_mutex); ucontrol->value.integer.value[0] = mout->share_spdif; return 0; } @@ -2537,9 +2539,15 @@ static int spdif_share_sw_get(struct snd_kcontrol *kcontrol, static int spdif_share_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); - mout->share_spdif = !!ucontrol->value.integer.value[0]; - return 0; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_multi_out *mout = (void *)kcontrol->private_value; + bool val = !!ucontrol->value.integer.value[0]; + int change; + + guard(mutex)(&codec->spdif_mutex); + change = mout->share_spdif != val; + mout->share_spdif = val; + return change; } static const struct snd_kcontrol_new spdif_share_sw = { @@ -2550,6 +2558,14 @@ static const struct snd_kcontrol_new spdif_share_sw = { .put = spdif_share_sw_put, }; +static void notify_spdif_share_sw(struct hda_codec *codec, + struct hda_multi_out *mout) +{ + if (mout->share_spdif_kctl) + snd_ctl_notify_one(codec->card, SNDRV_CTL_EVENT_MASK_VALUE, + mout->share_spdif_kctl, 0); +} + /** * snd_hda_create_spdif_share_sw - create Default PCM switch * @codec: the HDA codec @@ -2559,15 +2575,24 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec, struct hda_multi_out *mout) { struct snd_kcontrol *kctl; + int err; if (!mout->dig_out_nid) return 0; - kctl = snd_ctl_new1(&spdif_share_sw, mout); + kctl = snd_ctl_new1(&spdif_share_sw, codec); if (!kctl) return -ENOMEM; - /* ATTENTION: here mout is passed as private_data, instead of codec */ - return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl); + /* snd_ctl_new1() stores @codec in private_data; stash @mout in + * private_value for the share-switch callbacks and cache the + * assigned control for forced-disable notifications. + */ + kctl->private_value = (unsigned long)mout; + err = snd_hda_ctl_add(codec, mout->dig_out_nid, kctl); + if (err < 0) + return err; + mout->share_spdif_kctl = kctl; + return 0; } EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw); @@ -2768,9 +2793,9 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, if (codec->power_filter) state = codec->power_filter(codec, fg, state); if (state == power_state || power_state != AC_PWRST_D3) - snd_hda_codec_read(codec, fg, flags, - AC_VERB_SET_POWER_STATE, - state); + snd_hda_codec_write_sync(codec, fg, flags, + AC_VERB_SET_POWER_STATE, + state); snd_hda_codec_set_power_to_all(codec, fg, power_state); } state = snd_hda_sync_power_state(codec, fg, power_state); @@ -3701,6 +3726,8 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_pcm_stream *hinfo) { struct snd_pcm_runtime *runtime = substream->runtime; + bool notify_share_sw = false; + runtime->hw.channels_max = mout->max_channels; if (mout->dig_out_nid) { if (!mout->analog_rates) { @@ -3729,10 +3756,12 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, hinfo->maxbps = mout->spdif_maxbps; } else { mout->share_spdif = 0; - /* FIXME: need notify? */ + notify_share_sw = true; } } } + if (notify_share_sw) + notify_spdif_share_sw(codec, mout); return snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); } @@ -4024,6 +4053,35 @@ void snd_hda_bus_reset_codecs(struct hda_bus *bus) } /** + * snd_hda_codec_set_gpio - Set up GPIO bits for AFG + * @codec: the HDA codec + * @mask: GPIO bitmask + * @dir: GPIO direction bits + * @data: GPIO data bits + * @delay: the delay in msec before writing GPIO data bits + */ +void snd_hda_codec_set_gpio(struct hda_codec *codec, unsigned int mask, + unsigned int dir, unsigned int data, + unsigned int delay) +{ + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_MASK, mask); + if (delay) { + snd_hda_codec_write_sync(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DIRECTION, dir); + msleep(delay); + snd_hda_codec_write_sync(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, data); + } else { + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DIRECTION, dir); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, data); + } +} +EXPORT_SYMBOL_GPL(snd_hda_codec_set_gpio); + +/** * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer * @pcm: PCM caps bits * @buf: the string buffer to write diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h index ab423f1cef54..98b2c4acebc2 100644 --- a/sound/hda/common/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -221,6 +221,7 @@ struct hda_multi_out { unsigned int spdif_rates; unsigned int spdif_maxbps; u64 spdif_formats; + struct snd_kcontrol *share_spdif_kctl; /* cached shared SPDIF switch */ }; int snd_hda_create_spdif_share_sw(struct hda_codec *codec, diff --git a/sound/hda/common/proc.c b/sound/hda/common/proc.c index 3bc33c5617b2..c83796b13d3d 100644 --- a/sound/hda/common/proc.c +++ b/sound/hda/common/proc.c @@ -640,41 +640,78 @@ static void print_gpio(struct snd_info_buffer *buffer, { unsigned int gpio = param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); - unsigned int enable, direction, wake, unsol, sticky, data; - int i, max; + int i, gpio_max, gpo_max, gpi_max; + + gpio_max = gpio & AC_GPIO_IO_COUNT; + gpo_max = (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT; + gpi_max = (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT; + snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " "unsolicited=%d, wake=%d\n", - gpio & AC_GPIO_IO_COUNT, - (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT, - (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT, + gpio_max, gpo_max, gpi_max, (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0, (gpio & AC_GPIO_WAKE) ? 1 : 0); - max = gpio & AC_GPIO_IO_COUNT; - if (!max || max > 8) - return; - enable = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_MASK, 0); - direction = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - wake = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_WAKE_MASK, 0); - unsol = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0); - sticky = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_STICKY_MASK, 0); - data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DATA, 0); - for (i = 0; i < max; ++i) - snd_iprintf(buffer, - " IO[%d]: enable=%d, dir=%d, wake=%d, " - "sticky=%d, data=%d, unsol=%d\n", i, - (enable & (1<<i)) ? 1 : 0, - (direction & (1<<i)) ? 1 : 0, - (wake & (1<<i)) ? 1 : 0, - (sticky & (1<<i)) ? 1 : 0, - (data & (1<<i)) ? 1 : 0, - (unsol & (1<<i)) ? 1 : 0); - /* FIXME: add GPO and GPI pin information */ + + if (gpio_max && gpio_max <= 8) { + unsigned int enable, direction, wake, unsol, sticky, data; + + enable = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_MASK, 0); + direction = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + wake = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_WAKE_MASK, 0); + unsol = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, + 0); + sticky = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_STICKY_MASK, 0); + data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, 0); + for (i = 0; i < gpio_max; ++i) { + snd_iprintf(buffer, + " IO[%d]: enable=%d, dir=%d, wake=%d, ", + i, (enable & (1 << i)) ? 1 : 0, + (direction & (1 << i)) ? 1 : 0, + (wake & (1 << i)) ? 1 : 0); + snd_iprintf(buffer, + "sticky=%d, data=%d, unsol=%d\n", + (sticky & (1 << i)) ? 1 : 0, + (data & (1 << i)) ? 1 : 0, + (unsol & (1 << i)) ? 1 : 0); + } + } + + if (gpo_max && gpo_max <= 8) { + unsigned int gpo_data; + + gpo_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPO_DATA, 0); + for (i = 0; i < gpo_max; ++i) + snd_iprintf(buffer, " GPO[%d]: data=%d\n", i, + (gpo_data & (1 << i)) ? 1 : 0); + } + + if (gpi_max && gpi_max <= 8) { + unsigned int wake, unsol, sticky, data; + + wake = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPI_WAKE_MASK, 0); + unsol = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPI_UNSOLICITED_RSP_MASK, + 0); + sticky = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPI_STICKY_MASK, 0); + data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPI_DATA, 0); + for (i = 0; i < gpi_max; ++i) + snd_iprintf(buffer, " GPI[%d]: wake=%d, sticky=%d, data=%d, unsol=%d\n", + i, (wake & (1 << i)) ? 1 : 0, + (sticky & (1 << i)) ? 1 : 0, + (data & (1 << i)) ? 1 : 0, + (unsol & (1 << i)) ? 1 : 0); + } + print_nid_array(buffer, codec, nid, &codec->mixers); print_nid_array(buffer, codec, nid, &codec->nids); } @@ -940,4 +977,3 @@ int snd_hda_codec_proc_new(struct hda_codec *codec) snprintf(name, sizeof(name), "codec#%d", codec->core.addr); return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info); } - diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index a4104376523a..257c498c3260 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2436,20 +2436,7 @@ static void azx_remove(struct pci_dev *pci) /* cancel the pending probing work */ chip = card->private_data; hda = container_of(chip, struct hda_intel, chip); - /* FIXME: below is an ugly workaround. - * Both device_release_driver() and driver_probe_device() - * take *both* the device's and its parent's lock before - * calling the remove() and probe() callbacks. The codec - * probe takes the locks of both the codec itself and its - * parent, i.e. the PCI controller dev. Meanwhile, when - * the PCI controller is unbound, it takes its lock, too - * ==> ouch, a deadlock! - * As a workaround, we unlock temporarily here the controller - * device during cancel_work_sync() call. - */ - device_unlock(&pci->dev); cancel_delayed_work_sync(&hda->probe_work); - device_lock(&pci->dev); clear_bit(chip->dev_index, probed_devs); pci_set_drvdata(pci, NULL); diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index b24c80410d45..9dd36b82a6ac 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -53,6 +53,31 @@ static void ak4524_reset(struct snd_akm4xxx *ak, int state) } } +/* reset procedure for AK4529 */ +static void ak4529_reset(struct snd_akm4xxx *ak, int state) +{ + static const unsigned char regs[] = { + 0x0a, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x0b, 0x0c, 0x08, + }; + unsigned int i; + unsigned char reg; + + if (state) { + snd_akm4xxx_write(ak, 0, 0x09, + snd_akm4xxx_get(ak, 0, 0x09) & ~0x01); + return; + } + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + reg = regs[i]; + snd_akm4xxx_write(ak, 0, reg, + snd_akm4xxx_get(ak, 0, reg)); + } + snd_akm4xxx_write(ak, 0, 0x09, + snd_akm4xxx_get(ak, 0, 0x09) | 0x01); +} + /* reset procedure for AK4355 and AK4358 */ static void ak435X_reset(struct snd_akm4xxx *ak, int state) { @@ -99,7 +124,7 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state) ak4524_reset(ak, state); break; case SND_AK4529: - /* FIXME: needed for ak4529? */ + ak4529_reset(ak, state); break; case SND_AK4355: ak435X_reset(ak, state); @@ -256,6 +281,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) 0x07, 0x00, /* 7: ROUT muted */ 0xff, 0xff }; + static const unsigned char ak5365_defaults[] = { + 0x01, 0x00, 0x00, 0x2b, 0x7f, 0x7f, 0x28, 0x89, + }; int chip; const unsigned char *ptr, *inits; @@ -302,10 +330,12 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) ak->total_regs = 0x05; break; case SND_AK5365: - /* FIXME: any init sequence? */ ak->num_chips = 1; ak->name = "ak5365"; ak->total_regs = 0x08; + memcpy(ak->images, ak5365_defaults, sizeof(ak5365_defaults)); + snd_akm4xxx_set_vol(ak, 0, 0x04, 127); + snd_akm4xxx_set_vol(ak, 0, 0x05, 127); return; case SND_AK4620: inits = inits_ak4620; diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c index e8c50a036bea..39c5e87c6ab0 100644 --- a/sound/i2c/tea6330t.c +++ b/sound/i2c/tea6330t.c @@ -51,6 +51,7 @@ int snd_tea6330t_detect(struct snd_i2c_bus *bus, int equalizer) snd_i2c_unlock(bus); return res; } +EXPORT_SYMBOL(snd_tea6330t_detect); #if 0 static void snd_tea6330t_set(struct tea6330t *tea, @@ -355,6 +356,42 @@ int snd_tea6330t_update_mixer(struct snd_card *card, snd_i2c_device_free(device); return err; } - -EXPORT_SYMBOL(snd_tea6330t_detect); EXPORT_SYMBOL(snd_tea6330t_update_mixer); + +int snd_tea6330t_restore_mixer(struct snd_i2c_bus *bus) +{ + struct snd_i2c_device *device; + struct tea6330t *tea; + unsigned char bytes[7]; + unsigned int idx; + int err; + + if (!bus) + return -EINVAL; + + snd_i2c_lock(bus); + list_for_each_entry(device, &bus->devices, list) { + if (device->addr != TEA6330T_ADDR) + continue; + + tea = device->private_data; + if (!tea) { + err = -EINVAL; + goto unlock; + } + + bytes[0] = TEA6330T_SADDR_VOLUME_LEFT; + for (idx = 0; idx < 6; idx++) + bytes[idx + 1] = tea->regs[idx]; + err = snd_i2c_sendbytes(device, bytes, 7); + err = err < 0 ? err : 0; + goto unlock; + } + + err = -ENODEV; + +unlock: + snd_i2c_unlock(bus); + return err; +} +EXPORT_SYMBOL(snd_tea6330t_restore_mixer); diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c index 6a95dfb7600a..7255b34f9148 100644 --- a/sound/isa/es1688/es1688.c +++ b/sound/isa/es1688/es1688.c @@ -184,12 +184,44 @@ static int snd_es1688_isa_probe(struct device *dev, unsigned int n) return 0; } +#ifdef CONFIG_PM +static int snd_es1688_card_suspend(struct snd_card *card) +{ + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + return 0; +} + +static int snd_es1688_card_resume(struct snd_card *card) +{ + struct snd_es1688 *chip = card->private_data; + int err; + + err = snd_es1688_reset(chip); + if (err < 0) + return err; + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} + +static int snd_es1688_isa_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + return snd_es1688_card_suspend(dev_get_drvdata(dev)); +} + +static int snd_es1688_isa_resume(struct device *dev, unsigned int n) +{ + return snd_es1688_card_resume(dev_get_drvdata(dev)); +} +#endif + static struct isa_driver snd_es1688_driver = { .match = snd_es1688_match, .probe = snd_es1688_isa_probe, -#if 0 /* FIXME */ - .suspend = snd_es1688_suspend, - .resume = snd_es1688_resume, +#ifdef CONFIG_PM + .suspend = snd_es1688_isa_suspend, + .resume = snd_es1688_isa_resume, #endif .driver = { .name = DEV_NAME @@ -266,20 +298,12 @@ static void snd_es968_pnp_remove(struct pnp_card_link *pcard) static int snd_es968_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) { - struct snd_card *card = pnp_get_card_drvdata(pcard); - - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - return 0; + return snd_es1688_card_suspend(pnp_get_card_drvdata(pcard)); } static int snd_es968_pnp_resume(struct pnp_card_link *pcard) { - struct snd_card *card = pnp_get_card_drvdata(pcard); - struct snd_es1688 *chip = card->private_data; - - snd_es1688_reset(chip); - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - return 0; + return snd_es1688_card_resume(pnp_get_card_drvdata(pcard)); } #endif diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c index ffc69e26227e..30bd76eee96e 100644 --- a/sound/isa/gus/gus_dma.c +++ b/sound/isa/gus/gus_dma.c @@ -173,6 +173,39 @@ int snd_gf1_dma_done(struct snd_gus_card * gus) return 0; } +void snd_gf1_dma_suspend(struct snd_gus_card *gus) +{ + struct snd_gf1_dma_block *block; + + guard(mutex)(&gus->dma_mutex); + if (!gus->gf1.dma_shared) + return; + + snd_dma_disable(gus->gf1.dma1); + snd_gf1_dma_ack(gus); + if (gus->gf1.dma_ack) + gus->gf1.dma_ack(gus, gus->gf1.dma_private_data); + gus->gf1.dma_ack = NULL; + gus->gf1.dma_private_data = NULL; + + while ((block = gus->gf1.dma_data_pcm)) { + gus->gf1.dma_data_pcm = block->next; + if (block->ack) + block->ack(gus, block->private_data); + kfree(block); + } + while ((block = gus->gf1.dma_data_synth)) { + gus->gf1.dma_data_synth = block->next; + if (block->ack) + block->ack(gus, block->private_data); + kfree(block); + } + + gus->gf1.dma_data_pcm_last = NULL; + gus->gf1.dma_data_synth_last = NULL; + gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER; +} + int snd_gf1_dma_transfer_block(struct snd_gus_card * gus, struct snd_gf1_dma_block * __block, int atomic, diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index b2b189c83569..6adf8b698e2b 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -404,6 +404,42 @@ int snd_gus_initialize(struct snd_gus_card *gus) return 0; } +int snd_gus_suspend(struct snd_gus_card *gus) +{ + int err; + + if (gus->pcm) { + err = snd_pcm_suspend_all(gus->pcm); + if (err < 0) + return err; + } + + err = snd_gf1_suspend(gus); + if (err < 0) + return err; + + snd_power_change_state(gus->card, SNDRV_CTL_POWER_D3hot); + return 0; +} +EXPORT_SYMBOL(snd_gus_suspend); + +int snd_gus_resume(struct snd_gus_card *gus) +{ + int err; + + err = snd_gus_init_dma_irq(gus, 1); + if (err < 0) + return err; + + err = snd_gf1_resume(gus); + if (err < 0) + return err; + + snd_power_change_state(gus->card, SNDRV_CTL_POWER_D0); + return 0; +} +EXPORT_SYMBOL(snd_gus_resume); + /* gus_io.c */ EXPORT_SYMBOL(snd_gf1_delay); EXPORT_SYMBOL(snd_gf1_write8); diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index caf371897b78..a0757e1ede46 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -471,7 +471,8 @@ static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream, if (cmd == SNDRV_PCM_TRIGGER_START) { snd_gf1_pcm_trigger_up(substream); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + } else if (cmd == SNDRV_PCM_TRIGGER_STOP || + cmd == SNDRV_PCM_TRIGGER_SUSPEND) { scoped_guard(spinlock, &pcmp->lock) { pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; } @@ -558,7 +559,8 @@ static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream, if (cmd == SNDRV_PCM_TRIGGER_START) { val = gus->gf1.pcm_rcntrl_reg; - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + } else if (cmd == SNDRV_PCM_TRIGGER_STOP || + cmd == SNDRV_PCM_TRIGGER_SUSPEND) { val = 0; } else { return -EINVAL; @@ -856,4 +858,3 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) return 0; } - diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c index a7a3e764bb77..998fa245708c 100644 --- a/sound/isa/gus/gus_reset.c +++ b/sound/isa/gus/gus_reset.c @@ -6,6 +6,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/time.h> +#include <asm/dma.h> #include <sound/core.h> #include <sound/gus.h> @@ -263,11 +264,18 @@ void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice) private_free(voice); } -/* - * call this function only by start of driver - */ +static void snd_gf1_init_software_state(struct snd_gus_card *gus) +{ + unsigned int i; -int snd_gf1_start(struct snd_gus_card * gus) + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); + for (i = 0; i < 32; i++) { + gus->gf1.voices[i].number = i; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); + } +} + +static void snd_gf1_hw_start(struct snd_gus_card *gus, bool initial) { unsigned int i; @@ -277,14 +285,14 @@ int snd_gf1_start(struct snd_gus_card * gus) udelay(160); snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); - snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); - for (i = 0; i < 32; i++) { - gus->gf1.voices[i].number = i; - snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); + if (initial) { + snd_gf1_init_software_state(gus); + snd_gf1_uart_cmd(gus, 0x03); + } else { + guard(spinlock_irqsave)(&gus->uart_cmd_lock); + outb(0x03, GUSP(gus, MIDICTRL)); } - snd_gf1_uart_cmd(gus, 0x03); /* huh.. this cleanup took me some time... */ - if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); @@ -293,6 +301,8 @@ int snd_gf1_start(struct snd_gus_card * gus) snd_gf1_select_active_voices(gus); snd_gf1_delay(gus); gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8; + gus->gf1.hw_lfo = 0; + gus->gf1.sw_lfo = 0; /* initialize LFOs & clear LFOs memory */ if (gus->gf1.enh_mode && gus->gf1.memory) { gus->gf1.hw_lfo = 1; @@ -321,7 +331,15 @@ int snd_gf1_start(struct snd_gus_card * gus) outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); } +} +int snd_gf1_start(struct snd_gus_card *gus) +{ + /* + * Probe-time startup initializes both GF1 hardware and the + * software state that suspend/resume keeps across PM cycles. + */ + snd_gf1_hw_start(gus, true); snd_gf1_timers_init(gus); snd_gf1_look_regs(gus); snd_gf1_mem_init(gus); @@ -357,3 +375,27 @@ int snd_gf1_stop(struct snd_gus_card * gus) return 0; } + +int snd_gf1_suspend(struct snd_gus_card *gus) +{ + snd_gf1_dma_suspend(gus); + snd_gf1_uart_suspend(gus); + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); + snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); + snd_gf1_stop_voices(gus, 0, 31); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); + snd_dma_disable(gus->gf1.dma2); + + return 0; +} + +int snd_gf1_resume(struct snd_gus_card *gus) +{ + snd_gf1_hw_start(gus, false); + snd_gf1_timers_resume(gus); + snd_gf1_uart_resume(gus); + + return 0; +} diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c index e3a8847e02cf..14dcde138bc7 100644 --- a/sound/isa/gus/gus_timer.c +++ b/sound/isa/gus/gus_timer.c @@ -178,3 +178,17 @@ void snd_gf1_timers_done(struct snd_gus_card * gus) gus->gf1.timer2 = NULL; } } + +void snd_gf1_timers_resume(struct snd_gus_card *gus) +{ + if (gus->gf1.timer1) { + gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1; + if (gus->gf1.timer_enabled & 4) + snd_gf1_timer1_start(gus->gf1.timer1); + } + if (gus->gf1.timer2) { + gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2; + if (gus->gf1.timer_enabled & 8) + snd_gf1_timer2_start(gus->gf1.timer2); + } +} diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c index 770d8f3e4cff..25057a5a81b0 100644 --- a/sound/isa/gus/gus_uart.c +++ b/sound/isa/gus/gus_uart.c @@ -232,3 +232,50 @@ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device) gus->midi_uart = rmidi; return err; } + +void snd_gf1_uart_suspend(struct snd_gus_card *gus) +{ + guard(spinlock_irqsave)(&gus->uart_cmd_lock); + outb(0x03, GUSP(gus, MIDICTRL)); +} + +void snd_gf1_uart_resume(struct snd_gus_card *gus) +{ + unsigned short uart_cmd; + bool active; + int i; + + scoped_guard(spinlock_irqsave, &gus->uart_cmd_lock) { + active = gus->midi_substream_input || gus->midi_substream_output; + } + if (!active) + return; + + /* snd_gf1_hw_start() already left MIDICTRL in reset. */ + usleep_range(160, 200); + + guard(spinlock_irqsave)(&gus->uart_cmd_lock); + if (!gus->midi_substream_input && !gus->midi_substream_output) + return; + + if (gus->midi_substream_output) + gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; + if (gus->midi_substream_input) + gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; + + if (!gus->uart_enable) + return; + + uart_cmd = gus->gf1.uart_cmd; + snd_gf1_uart_cmd(gus, 0x00); + + if (gus->midi_substream_input) { + for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) + snd_gf1_uart_get(gus); + if (i >= 1000) + dev_err(gus->card->dev, + "gus midi uart resume - cleanup error\n"); + } + + snd_gf1_uart_cmd(gus, uart_cmd); +} diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c index 101202acefb3..363c819ced89 100644 --- a/sound/isa/gus/gusclassic.c +++ b/sound/isa/gus/gusclassic.c @@ -145,6 +145,7 @@ static int snd_gusclassic_probe(struct device *dev, unsigned int n) error = snd_gusclassic_create(card, dev, n, &gus); if (error < 0) return error; + card->private_data = gus; error = snd_gusclassic_detect(gus); if (error < 0) @@ -193,11 +194,29 @@ static int snd_gusclassic_probe(struct device *dev, unsigned int n) return 0; } +#ifdef CONFIG_PM +static int snd_gusclassic_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + struct snd_card *card = dev_get_drvdata(dev); + + return snd_gus_suspend(card->private_data); +} + +static int snd_gusclassic_resume(struct device *dev, unsigned int n) +{ + struct snd_card *card = dev_get_drvdata(dev); + + return snd_gus_resume(card->private_data); +} +#endif + static struct isa_driver snd_gusclassic_driver = { .match = snd_gusclassic_match, .probe = snd_gusclassic_probe, -#if 0 /* FIXME */ +#ifdef CONFIG_PM .suspend = snd_gusclassic_suspend, + .resume = snd_gusclassic_resume, #endif .driver = { .name = DEV_NAME diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c index ed921b89b00a..0984731740c4 100644 --- a/sound/isa/gus/gusextreme.c +++ b/sound/isa/gus/gusextreme.c @@ -44,6 +44,11 @@ static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; +struct snd_gusextreme { + struct snd_es1688 es1688; + struct snd_gus_card *gus; +}; + module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard."); module_param_array(id, charp, NULL, 0444); @@ -142,17 +147,15 @@ static int snd_gusextreme_gus_card_create(struct snd_card *card, 0, channels[n], pcm_channels[n], 0, rgus); } -static int snd_gusextreme_detect(struct snd_gus_card *gus, - struct snd_es1688 *es1688) +static void snd_gusextreme_enable_gf1(struct snd_gus_card *gus, + struct snd_es1688 *es1688) { - unsigned char d; - /* * This is main stuff - enable access to GF1 chip... * I'm not sure, if this will work for card which have * ES1688 chip in another place than 0x220. - * - * I used reverse-engineering in DOSEMU. [--jk] + * + * I used reverse-engineering in DOSEMU. [--jk] * * ULTRINIT.EXE: * 0x230 = 0,2,3 @@ -172,7 +175,14 @@ static int snd_gusextreme_detect(struct snd_gus_card *gus, outb(0, 0x201); outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1)); } +} + +static int snd_gusextreme_detect(struct snd_gus_card *gus, + struct snd_es1688 *es1688) +{ + unsigned char d; + snd_gusextreme_enable_gf1(gus, es1688); udelay(100); snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ @@ -223,16 +233,18 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n) { struct snd_card *card; struct snd_gus_card *gus; + struct snd_gusextreme *gusextreme; struct snd_es1688 *es1688; struct snd_opl3 *opl3; int error; error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, - sizeof(struct snd_es1688), &card); + sizeof(*gusextreme), &card); if (error < 0) return error; - es1688 = card->private_data; + gusextreme = card->private_data; + es1688 = &gusextreme->es1688; if (mpu_port[n] == SNDRV_AUTO_PORT) mpu_port[n] = 0; @@ -250,6 +262,7 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n) error = snd_gusextreme_gus_card_create(card, dev, n, &gus); if (error < 0) return error; + gusextreme->gus = gus; error = snd_gusextreme_detect(gus, es1688); if (error < 0) @@ -321,10 +334,36 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n) return 0; } +#ifdef CONFIG_PM +static int snd_gusextreme_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct snd_gusextreme *gusextreme = card->private_data; + + return snd_gus_suspend(gusextreme->gus); +} + +static int snd_gusextreme_resume(struct device *dev, unsigned int n) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct snd_gusextreme *gusextreme = card->private_data; + int err; + + err = snd_es1688_reset(&gusextreme->es1688); + if (err < 0) + return err; + + snd_gusextreme_enable_gf1(gusextreme->gus, &gusextreme->es1688); + usleep_range(100, 200); + return snd_gus_resume(gusextreme->gus); +} +#endif + static struct isa_driver snd_gusextreme_driver = { .match = snd_gusextreme_match, .probe = snd_gusextreme_probe, -#if 0 /* FIXME */ +#ifdef CONFIG_PM .suspend = snd_gusextreme_suspend, .resume = snd_gusextreme_resume, #endif diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index b572411c4422..f1fd7ff2121d 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -328,12 +328,38 @@ static int snd_gusmax_probe(struct device *pdev, unsigned int dev) return 0; } +#ifdef CONFIG_PM +static int snd_gusmax_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct snd_gusmax *maxcard = card->private_data; + + maxcard->wss->suspend(maxcard->wss); + return snd_gus_suspend(maxcard->gus); +} + +static int snd_gusmax_resume(struct device *dev, unsigned int n) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct snd_gusmax *maxcard = card->private_data; + + /* Restore the board routing latch before resuming the codec and GF1. */ + outb(maxcard->gus->max_cntrl_val, GUSP(maxcard->gus, MAXCNTRLPORT)); + maxcard->wss->resume(maxcard->wss); + return snd_gus_resume(maxcard->gus); +} +#endif + #define DEV_NAME "gusmax" static struct isa_driver snd_gusmax_driver = { .match = snd_gusmax_match, .probe = snd_gusmax_probe, - /* FIXME: suspend/resume */ +#ifdef CONFIG_PM + .suspend = snd_gusmax_suspend, + .resume = snd_gusmax_resume, +#endif .driver = { .name = DEV_NAME }, diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 18adcd35e117..6c3a2977dcb3 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -96,6 +96,7 @@ struct snd_interwave { struct snd_gus_card *gus; struct snd_wss *wss; #ifdef SNDRV_STB + struct snd_i2c_bus *i2c_bus; struct resource *i2c_res; #endif unsigned short gus_status_reg; @@ -363,18 +364,30 @@ struct rom_hdr { /* 511 */ unsigned char csum; }; -static void snd_interwave_detect_memory(struct snd_gus_card *gus) +static const unsigned int snd_interwave_memory_configs[] = { + 0x00000001, 0x00000101, 0x01010101, 0x00000401, + 0x04040401, 0x00040101, 0x04040101, 0x00000004, + 0x00000404, 0x04040404, 0x00000010, 0x00001010, + 0x10101010 +}; + +static int snd_interwave_find_memory_config(unsigned int lmct) { - static const unsigned int lmc[13] = - { - 0x00000001, 0x00000101, 0x01010101, 0x00000401, - 0x04040401, 0x00040101, 0x04040101, 0x00000004, - 0x00000404, 0x04040404, 0x00000010, 0x00001010, - 0x10101010 - }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(snd_interwave_memory_configs); i++) { + if (lmct == snd_interwave_memory_configs[i]) + return i; + } + + return -EINVAL; +} +static void snd_interwave_detect_memory(struct snd_gus_card *gus) +{ int bank_pos, pages; unsigned int i, lmct; + int lmc_cfg; int psizes[4]; unsigned char iwave[8]; unsigned char csum; @@ -399,17 +412,20 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus) #if 0 dev_dbg(gus->card->dev, "lmct = 0x%08x\n", lmct); #endif - for (i = 0; i < ARRAY_SIZE(lmc); i++) - if (lmct == lmc[i]) { + lmc_cfg = snd_interwave_find_memory_config(lmct); + if (lmc_cfg >= 0) { #if 0 - dev_dbg(gus->card->dev, "found !!! %i\n", i); + dev_dbg(gus->card->dev, "found !!! %i\n", lmc_cfg); #endif - snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i); - snd_interwave_bank_sizes(gus, psizes); - break; - } - if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode) - snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, + (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | + lmc_cfg); + snd_interwave_bank_sizes(gus, psizes); + } else if (!gus->gf1.enh_mode) { + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, + (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | + 2); + } for (i = 0; i < 4; i++) { gus->gf1.mem_alloc.banks_8[i].address = gus->gf1.mem_alloc.banks_16[i].address = i << 22; @@ -454,24 +470,28 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus) snd_interwave_reset(gus); } +static void __snd_interwave_restore_regs(struct snd_gus_card *gus) +{ + snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); + snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); + snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); +} + static void snd_interwave_init(int dev, struct snd_gus_card *gus) { - /* ok.. some InterWave specific initialization */ + /* Probe-time setup also clears the timer control register. */ scoped_guard(spinlock_irqsave, &gus->reg_lock) { snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00); - snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); - snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); - snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); - snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); - snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); - snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); + __snd_interwave_restore_regs(gus); } gus->equal_irq = 1; gus->codec_flag = 1; gus->interwave = 1; gus->max_flag = 1; gus->joystick_dac = joystick_dac[dev]; - } static const struct snd_kcontrol_new snd_interwave_controls[] = { @@ -724,6 +744,7 @@ static int snd_interwave_probe(struct snd_card *card, int dev, err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1); if (err < 0) return err; + iwcard->i2c_bus = i2c_bus; } #endif @@ -828,10 +849,97 @@ static int snd_interwave_isa_probe(struct device *pdev, return 0; } +#ifdef CONFIG_PM +static void snd_interwave_restore_regs(struct snd_gus_card *gus) +{ + scoped_guard(spinlock_irqsave, &gus->reg_lock) + __snd_interwave_restore_regs(gus); +} + +static void snd_interwave_restore_memory(struct snd_gus_card *gus) +{ + unsigned short mem_cfg; + unsigned int lmct = 0; + int i, lmc_cfg; + + if (!gus->gf1.memory) + return; + + for (i = 0; i < 4; i++) + lmct |= (gus->gf1.mem_alloc.banks_16[i].size >> 18) << (i * 8); + + lmc_cfg = snd_interwave_find_memory_config(lmct); + if (lmc_cfg < 0) { + if (!gus->gf1.enh_mode) { + lmc_cfg = 2; + } else { + dev_warn(gus->card->dev, + "cannot restore InterWave memory layout 0x%08x\n", + lmct); + return; + } + } + + scoped_guard(spinlock_irqsave, &gus->reg_lock) { + mem_cfg = snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG); + mem_cfg = (mem_cfg & 0xfff0) | lmc_cfg; + mem_cfg = (mem_cfg & 0xff1f) | (4 << 5); + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, mem_cfg); + } +} + +static int snd_interwave_card_suspend(struct snd_card *card) +{ + struct snd_interwave *iwcard = card->private_data; + + iwcard->wss->suspend(iwcard->wss); + return snd_gus_suspend(iwcard->gus); +} + +static int snd_interwave_card_resume(struct snd_card *card) +{ + struct snd_interwave *iwcard = card->private_data; + int err; + + err = snd_gus_resume(iwcard->gus); + if (err < 0) + return err; + + snd_interwave_restore_regs(iwcard->gus); + snd_interwave_restore_memory(iwcard->gus); + iwcard->wss->resume(iwcard->wss); +#ifdef SNDRV_STB + if (iwcard->i2c_bus) { + err = snd_tea6330t_restore_mixer(iwcard->i2c_bus); + if (err < 0) + dev_warn(card->dev, + "failed to restore TEA6330T mixer state: %d\n", + err); + } +#endif + + return 0; +} + +static int snd_interwave_isa_suspend(struct device *pdev, unsigned int dev, + pm_message_t state) +{ + return snd_interwave_card_suspend(dev_get_drvdata(pdev)); +} + +static int snd_interwave_isa_resume(struct device *pdev, unsigned int dev) +{ + return snd_interwave_card_resume(dev_get_drvdata(pdev)); +} +#endif + static struct isa_driver snd_interwave_driver = { .match = snd_interwave_isa_match, .probe = snd_interwave_isa_probe, - /* FIXME: suspend,resume */ +#ifdef CONFIG_PM + .suspend = snd_interwave_isa_suspend, + .resume = snd_interwave_isa_resume, +#endif .driver = { .name = INTERWAVE_DRIVER }, @@ -871,12 +979,28 @@ static int snd_interwave_pnp_detect(struct pnp_card_link *pcard, return 0; } +#ifdef CONFIG_PM +static int snd_interwave_pnpc_suspend(struct pnp_card_link *pcard, + pm_message_t state) +{ + return snd_interwave_card_suspend(pnp_get_card_drvdata(pcard)); +} + +static int snd_interwave_pnpc_resume(struct pnp_card_link *pcard) +{ + return snd_interwave_card_resume(pnp_get_card_drvdata(pcard)); +} +#endif + static struct pnp_card_driver interwave_pnpc_driver = { .flags = PNP_DRIVER_RES_DISABLE, .name = INTERWAVE_PNP_DRIVER, .id_table = snd_interwave_pnpids, .probe = snd_interwave_pnp_detect, - /* FIXME: suspend,resume */ +#ifdef CONFIG_PM + .suspend = snd_interwave_pnpc_suspend, + .resume = snd_interwave_pnpc_resume, +#endif }; #endif /* CONFIG_PNP */ diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c index 5e350234d572..77367e102fda 100644 --- a/sound/isa/msnd/msnd.c +++ b/sound/isa/msnd/msnd.c @@ -127,11 +127,8 @@ int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len) } EXPORT_SYMBOL(snd_msnd_upload_host); -int snd_msnd_enable_irq(struct snd_msnd *dev) +static int __snd_msnd_enable_irq(struct snd_msnd *dev) { - if (dev->irq_ref++) - return 0; - dev_dbg(dev->card->dev, LOGNAME ": Enabling IRQ\n"); guard(spinlock_irqsave)(&dev->lock); @@ -152,17 +149,9 @@ int snd_msnd_enable_irq(struct snd_msnd *dev) return -EIO; } -EXPORT_SYMBOL(snd_msnd_enable_irq); -int snd_msnd_disable_irq(struct snd_msnd *dev) +static int __snd_msnd_disable_irq(struct snd_msnd *dev) { - if (--dev->irq_ref > 0) - return 0; - - if (dev->irq_ref < 0) - dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n", - dev->irq_ref); - dev_dbg(dev->card->dev, LOGNAME ": Disabling IRQ\n"); guard(spinlock_irqsave)(&dev->lock); @@ -178,8 +167,39 @@ int snd_msnd_disable_irq(struct snd_msnd *dev) return -EIO; } + +int snd_msnd_enable_irq(struct snd_msnd *dev) +{ + if (dev->irq_ref++) + return 0; + + return __snd_msnd_enable_irq(dev); +} +EXPORT_SYMBOL(snd_msnd_enable_irq); + +int snd_msnd_disable_irq(struct snd_msnd *dev) +{ + if (--dev->irq_ref > 0) + return 0; + + if (dev->irq_ref < 0) + dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n", + dev->irq_ref); + + return __snd_msnd_disable_irq(dev); +} EXPORT_SYMBOL(snd_msnd_disable_irq); +int snd_msnd_force_irq(struct snd_msnd *dev, bool enable) +{ + if (!dev->irq_ref) + return 0; + + return enable ? __snd_msnd_enable_irq(dev) : + __snd_msnd_disable_irq(dev); +} +EXPORT_SYMBOL(snd_msnd_force_irq); + static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size) { long tmp = (size * HZ * chip->play_sample_size) / 8; @@ -507,25 +527,27 @@ static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_msnd *chip = snd_pcm_substream_chip(substream); - int result = 0; - if (cmd == SNDRV_PCM_TRIGGER_START) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: dev_dbg(chip->card->dev, "%s(START)\n", __func__); chip->banksPlayed = 0; set_bit(F_WRITING, &chip->flags); snd_msnd_DAPQ(chip, 1); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: dev_dbg(chip->card->dev, "%s(STOP)\n", __func__); - /* interrupt diagnostic, comment this out later */ clear_bit(F_WRITING, &chip->flags); snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP); - } else { + break; + default: dev_dbg(chip->card->dev, "%s(?????)\n", __func__); - result = -EINVAL; + return -EINVAL; } dev_dbg(chip->card->dev, "%s() ENDE\n", __func__); - return result; + return 0; } static snd_pcm_uframes_t @@ -589,17 +611,22 @@ static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream, { struct snd_msnd *chip = snd_pcm_substream_chip(substream); - if (cmd == SNDRV_PCM_TRIGGER_START) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: chip->last_recbank = -1; set_bit(F_READING, &chip->flags); if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0) return 0; clear_bit(F_READING, &chip->flags); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: clear_bit(F_READING, &chip->flags); snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP); return 0; + default: + break; } return -EINVAL; } @@ -668,4 +695,3 @@ EXPORT_SYMBOL(snd_msnd_pcm); MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers"); MODULE_LICENSE("GPL"); - diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h index 3d7810ed9186..56a700e6a5cb 100644 --- a/sound/isa/msnd/msnd.h +++ b/sound/isa/msnd/msnd.h @@ -253,6 +253,8 @@ struct snd_msnd { spinlock_t mixer_lock; int nresets; unsigned recsrc; + u8 pm_recsrc; + bool pm_mpu_input; #define LEVEL_ENTRIES 32 int left_levels[LEVEL_ENTRIES]; int right_levels[LEVEL_ENTRIES]; @@ -280,6 +282,7 @@ int snd_msnd_upload_host(struct snd_msnd *chip, const u8 *bin, int len); int snd_msnd_enable_irq(struct snd_msnd *chip); int snd_msnd_disable_irq(struct snd_msnd *chip); +int snd_msnd_force_irq(struct snd_msnd *chip, bool enable); void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file); int snd_msnd_DAPQ(struct snd_msnd *chip, int start); int snd_msnd_DARQ(struct snd_msnd *chip, int start); diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index c4eec391cd29..5b729bb02ef6 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -513,6 +513,19 @@ static void snd_msnd_mpu401_close(struct snd_mpu401 *mpu) snd_msnd_disable_irq(mpu->private_data); } +#ifdef CONFIG_PM +static u8 snd_msnd_pm_recsrc(struct snd_msnd *chip) +{ + /* Convert recsrc to the Capture Source selector: 0=Analog, 1=MASS, 2=SPDIF. */ + if (chip->recsrc & BIT(4)) + return 1; + if ((chip->recsrc & BIT(17)) && + test_bit(F_HAVEDIGITAL, &chip->flags)) + return 2; + return 0; +} +#endif + static long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; @@ -1001,10 +1014,73 @@ static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx) return 0; } +#ifdef CONFIG_PM +static int snd_msnd_card_suspend(struct snd_card *card) +{ + struct snd_msnd *chip = card->private_data; + struct snd_mpu401 *mpu; + int err; + + mpu = chip->rmidi ? chip->rmidi->private_data : NULL; + chip->pm_recsrc = snd_msnd_pm_recsrc(chip); + chip->pm_mpu_input = mpu && test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); + if (chip->pm_mpu_input) + snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_STOP); + + err = snd_msnd_force_irq(chip, false); + if (err < 0) { + if (chip->pm_mpu_input) + snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_START); + return err; + } + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + return 0; +} + +static int snd_msnd_card_resume(struct snd_card *card) +{ + struct snd_msnd *chip = card->private_data; + int err; + + err = snd_msnd_initialize(card); + if (err < 0) + return err; + + snd_msnd_calibrate_adc(chip, chip->play_sample_rate); + snd_msndmix_force_recsrc(chip, chip->pm_recsrc); + + err = snd_msnd_force_irq(chip, true); + if (err < 0) + return err; + + if (chip->pm_mpu_input) + snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_START); + + chip->nresets = 0; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} + +static int snd_msnd_isa_suspend(struct device *dev, unsigned int idx, + pm_message_t state) +{ + return snd_msnd_card_suspend(dev_get_drvdata(dev)); +} + +static int snd_msnd_isa_resume(struct device *dev, unsigned int idx) +{ + return snd_msnd_card_resume(dev_get_drvdata(dev)); +} +#endif + static struct isa_driver snd_msnd_driver = { .match = snd_msnd_isa_match, .probe = snd_msnd_isa_probe, - /* FIXME: suspend, resume */ +#ifdef CONFIG_PM + .suspend = snd_msnd_isa_suspend, + .resume = snd_msnd_isa_resume, +#endif .driver = { .name = DEV_NAME }, @@ -1111,6 +1187,18 @@ static int snd_msnd_pnp_detect(struct pnp_card_link *pcard, return 0; } +#ifdef CONFIG_PM +static int snd_msnd_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) +{ + return snd_msnd_card_suspend(pnp_get_card_drvdata(pcard)); +} + +static int snd_msnd_pnp_resume(struct pnp_card_link *pcard) +{ + return snd_msnd_card_resume(pnp_get_card_drvdata(pcard)); +} +#endif + static int isa_registered; static int pnp_registered; @@ -1127,6 +1215,10 @@ static struct pnp_card_driver msnd_pnpc_driver = { .name = "msnd_pinnacle", .id_table = msnd_pnpids, .probe = snd_msnd_pnp_detect, +#ifdef CONFIG_PM + .suspend = snd_msnd_pnp_suspend, + .resume = snd_msnd_pnp_resume, +#endif }; #endif /* CONFIG_PNP */ @@ -1161,4 +1253,3 @@ static void __exit snd_msnd_exit(void) module_init(snd_msnd_init); module_exit(snd_msnd_exit); - diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c index ec354483b9f8..8ca987221753 100644 --- a/sound/isa/msnd/msnd_pinnacle_mixer.c +++ b/sound/isa/msnd/msnd_pinnacle_mixer.c @@ -310,6 +310,10 @@ EXPORT_SYMBOL(snd_msndmix_new); void snd_msndmix_setup(struct snd_msnd *dev) { + writew(dev->left_levels[MSND_MIXER_VOLUME], + dev->SMA + SMA_wCurrMastVolLeft); + writew(dev->right_levels[MSND_MIXER_VOLUME], + dev->SMA + SMA_wCurrMastVolRight); update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); update_volm(MSND_MIXER_PCM, wCurrPlayVol); diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 6d618cc2ba45..cd3a63c7c2a7 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -100,6 +100,16 @@ MODULE_PARM_DESC(joystick, "Enable gameport."); #define PFX "sc6000: " #define DRV_NAME "SC-6000" +struct snd_sc6000 { + char __iomem *vport; + char __iomem *vmss_port; + struct snd_wss *chip; + u8 mss_config; + u8 config; + u8 hw_cfg[2]; + bool old_dsp; +}; + /* hardware dependent functions */ /* @@ -267,7 +277,7 @@ static int sc6000_dsp_reset(char __iomem *vport) /* detection and initialization */ static int sc6000_hw_cfg_write(struct device *devptr, - char __iomem *vport, const int *cfg) + char __iomem *vport, const u8 *cfg) { if (sc6000_write(devptr, vport, COMMAND_6C) < 0) { dev_warn(devptr, "CMD 0x%x: failed!\n", COMMAND_6C); @@ -353,8 +363,7 @@ static int sc6000_init_mss(struct device *devptr, return 0; } -static void sc6000_hw_cfg_encode(struct device *devptr, - char __iomem *vport, int *cfg, +static void sc6000_hw_cfg_encode(struct device *devptr, u8 *cfg, long xport, long xmpu, long xmss_port, int joystick) { @@ -376,27 +385,83 @@ static void sc6000_hw_cfg_encode(struct device *devptr, dev_dbg(devptr, "hw cfg %x, %x\n", cfg[0], cfg[1]); } -static int sc6000_init_board(struct device *devptr, - char __iomem *vport, - char __iomem *vmss_port, int dev) +static void sc6000_prepare_board(struct device *devptr, + struct snd_sc6000 *sc6000, + unsigned int dev, int xirq, int xdma) +{ + sc6000->mss_config = sc6000_irq_to_softcfg(xirq) | + sc6000_dma_to_softcfg(xdma); + sc6000->config = sc6000->mss_config | + sc6000_mpu_irq_to_softcfg(mpu_irq[dev]); + sc6000_hw_cfg_encode(devptr, sc6000->hw_cfg, port[dev], mpu_port[dev], + mss_port[dev], joystick[dev]); +} + +static void sc6000_detect_old_dsp(struct device *devptr, + struct snd_sc6000 *sc6000) +{ + sc6000_write(devptr, sc6000->vport, COMMAND_5C); + sc6000->old_dsp = sc6000_read(sc6000->vport) < 0; +} + +static int sc6000_program_board(struct device *devptr, + struct snd_sc6000 *sc6000) +{ + int err; + + if (!sc6000->old_dsp) { + if (sc6000_hw_cfg_write(devptr, sc6000->vport, + sc6000->hw_cfg) < 0) { + dev_err(devptr, "sc6000_hw_cfg_write: failed!\n"); + return -EIO; + } + } + + err = sc6000_setup_board(devptr, sc6000->vport, sc6000->config); + if (err < 0) { + dev_err(devptr, "sc6000_setup_board: failed!\n"); + return -ENODEV; + } + + sc6000_dsp_reset(sc6000->vport); + + if (!sc6000->old_dsp) { + sc6000_write(devptr, sc6000->vport, COMMAND_60); + sc6000_write(devptr, sc6000->vport, 0x02); + sc6000_dsp_reset(sc6000->vport); + } + + err = sc6000_setup_board(devptr, sc6000->vport, sc6000->config); + if (err < 0) { + dev_err(devptr, "sc6000_setup_board: failed!\n"); + return -ENODEV; + } + + err = sc6000_init_mss(devptr, sc6000->vport, sc6000->config, + sc6000->vmss_port, sc6000->mss_config); + if (err < 0) { + dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n"); + return -ENODEV; + } + + return 0; +} + +static int sc6000_init_board(struct device *devptr, struct snd_sc6000 *sc6000) { char answer[15]; char version[2]; - int mss_config = sc6000_irq_to_softcfg(irq[dev]) | - sc6000_dma_to_softcfg(dma[dev]); - int config = mss_config | - sc6000_mpu_irq_to_softcfg(mpu_irq[dev]); int err; - int old = 0; - err = sc6000_dsp_reset(vport); + err = sc6000_dsp_reset(sc6000->vport); if (err < 0) { dev_err(devptr, "sc6000_dsp_reset: failed!\n"); return err; } memset(answer, 0, sizeof(answer)); - err = sc6000_dsp_get_answer(devptr, vport, GET_DSP_COPYRIGHT, answer, 15); + err = sc6000_dsp_get_answer(devptr, sc6000->vport, GET_DSP_COPYRIGHT, + answer, 15); if (err <= 0) { dev_err(devptr, "sc6000_dsp_copyright: failed!\n"); return -ENODEV; @@ -408,54 +473,17 @@ static int sc6000_init_board(struct device *devptr, if (strncmp("SC-6000", answer, 7)) dev_warn(devptr, "Warning: non SC-6000 audio card!\n"); - if (sc6000_dsp_get_answer(devptr, vport, GET_DSP_VERSION, version, 2) < 2) { + if (sc6000_dsp_get_answer(devptr, sc6000->vport, + GET_DSP_VERSION, version, 2) < 2) { dev_err(devptr, "sc6000_dsp_version: failed!\n"); return -ENODEV; } dev_info(devptr, "Detected model: %s, DSP version %d.%d\n", answer, version[0], version[1]); - /* set configuration */ - sc6000_write(devptr, vport, COMMAND_5C); - if (sc6000_read(vport) < 0) - old = 1; - - if (!old) { - int cfg[2]; - sc6000_hw_cfg_encode(devptr, - vport, &cfg[0], port[dev], mpu_port[dev], - mss_port[dev], joystick[dev]); - if (sc6000_hw_cfg_write(devptr, vport, cfg) < 0) { - dev_err(devptr, "sc6000_hw_cfg_write: failed!\n"); - return -EIO; - } - } - err = sc6000_setup_board(devptr, vport, config); - if (err < 0) { - dev_err(devptr, "sc6000_setup_board: failed!\n"); - return -ENODEV; - } - - sc6000_dsp_reset(vport); - - if (!old) { - sc6000_write(devptr, vport, COMMAND_60); - sc6000_write(devptr, vport, 0x02); - sc6000_dsp_reset(vport); - } + sc6000_detect_old_dsp(devptr, sc6000); - err = sc6000_setup_board(devptr, vport, config); - if (err < 0) { - dev_err(devptr, "sc6000_setup_board: failed!\n"); - return -ENODEV; - } - err = sc6000_init_mss(devptr, vport, config, vmss_port, mss_config); - if (err < 0) { - dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n"); - return -ENODEV; - } - - return 0; + return sc6000_program_board(devptr, sc6000); } static int snd_sc6000_mixer(struct snd_wss *chip) @@ -538,10 +566,10 @@ static int snd_sc6000_match(struct device *devptr, unsigned int dev) static void snd_sc6000_free(struct snd_card *card) { - char __iomem *vport = (char __force __iomem *)card->private_data; + struct snd_sc6000 *sc6000 = card->private_data; - if (vport) - sc6000_setup_board(card->dev, vport, 0); + if (sc6000->vport) + sc6000_setup_board(card->dev, sc6000->vport, 0); } static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) @@ -552,15 +580,17 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) int xirq = irq[dev]; int xdma = dma[dev]; struct snd_card *card; + struct snd_sc6000 *sc6000; struct snd_wss *chip; struct snd_opl3 *opl3; char __iomem *vport; char __iomem *vmss_port; err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE, - 0, &card); + sizeof(*sc6000), &card); if (err < 0) return err; + sc6000 = card->private_data; if (xirq == SNDRV_AUTO_IRQ) { xirq = snd_legacy_find_free_irq(possible_irqs); @@ -587,7 +617,7 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) dev_err(devptr, "I/O port cannot be iomapped.\n"); return -EBUSY; } - card->private_data = (void __force *)vport; + sc6000->vport = vport; /* to make it marked as used */ if (!devm_request_region(devptr, mss_port[dev], 4, DRV_NAME)) { @@ -600,12 +630,15 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) dev_err(devptr, "MSS port I/O cannot be iomapped.\n"); return -EBUSY; } + sc6000->vmss_port = vmss_port; dev_dbg(devptr, "Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n", port[dev], xirq, xdma, mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]); - err = sc6000_init_board(devptr, vport, vmss_port, dev); + sc6000_prepare_board(devptr, sc6000, dev, xirq, xdma); + + err = sc6000_init_board(devptr, sc6000); if (err < 0) return err; card->private_free = snd_sc6000_free; @@ -614,6 +647,7 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) WSS_HW_DETECT, 0, &chip); if (err < 0) return err; + sc6000->chip = chip; err = snd_wss_pcm(chip, 0); if (err < 0) { @@ -670,10 +704,47 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev) return snd_card_free_on_error(devptr, __snd_sc6000_probe(devptr, dev)); } +#ifdef CONFIG_PM +static int snd_sc6000_suspend(struct device *devptr, unsigned int dev, + pm_message_t state) +{ + struct snd_card *card = dev_get_drvdata(devptr); + struct snd_sc6000 *sc6000 = card->private_data; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + sc6000->chip->suspend(sc6000->chip); + return 0; +} + +static int snd_sc6000_resume(struct device *devptr, unsigned int dev) +{ + struct snd_card *card = dev_get_drvdata(devptr); + struct snd_sc6000 *sc6000 = card->private_data; + int err; + + err = sc6000_dsp_reset(sc6000->vport); + if (err < 0) { + dev_err(devptr, "sc6000_dsp_reset: failed!\n"); + return err; + } + + err = sc6000_program_board(devptr, sc6000); + if (err < 0) + return err; + + sc6000->chip->resume(sc6000->chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif + static struct isa_driver snd_sc6000_driver = { .match = snd_sc6000_match, .probe = snd_sc6000_probe, - /* FIXME: suspend/resume */ +#ifdef CONFIG_PM + .suspend = snd_sc6000_suspend, + .resume = snd_sc6000_resume, +#endif .driver = { .name = DRV_NAME, }, diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index a31ca75774a6..553ceb92d298 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -131,6 +131,11 @@ enum card_type { struct soundscape { spinlock_t lock; unsigned io_base; + unsigned long wss_base; + int irq; + int mpu_irq; + int dma1; + int dma2; int ic_type; enum card_type type; struct resource *io_res; @@ -138,6 +143,8 @@ struct soundscape { struct snd_wss *chip; unsigned char midi_vol; + bool joystick; + bool midi_enabled; struct device *dev; }; @@ -150,6 +157,21 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c) } /* + * Store the resolved board settings in the per-card state so that + * the same configuration can be replayed later if necessary. + */ +static void sscape_store_settings(struct soundscape *sscape, int dev) +{ + sscape->io_base = port[dev]; + sscape->wss_base = wss_port[dev]; + sscape->irq = irq[dev]; + sscape->mpu_irq = mpu_irq[dev]; + sscape->dma1 = dma[dev]; + sscape->dma2 = dma2[dev]; + sscape->joystick = joystick[dev]; +} + +/* * Allocates some kernel memory that we can use for DMA. * I think this means that the memory has to map to * contiguous pages of physical memory. @@ -263,34 +285,36 @@ static int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout) /* * Write to the SoundScape's host-mode control registers, but - * leave any locking issues to the caller ... + * leave any locking issues to the caller. Returns true if + * the write succeeded. */ -static inline int host_write_unsafe(unsigned io_base, unsigned char data) +static inline bool host_write_unsafe(unsigned int io_base, unsigned char data) { if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) { outb(data, HOST_DATA_IO(io_base)); - return 1; + return true; } - return 0; + return false; } /* * Write to the SoundScape's host-mode control registers, performing * a limited amount of busy-waiting if the register isn't ready. - * Also leaves all locking-issues to the caller ... + * Also leaves all locking-issues to the caller. Returns true if + * the write succeeded before timing out. */ -static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data, - unsigned timeout) +static bool host_write_ctrl_unsafe(unsigned int io_base, unsigned char data, + unsigned int timeout) { - int err; + bool written; - while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) { + while (!(written = host_write_unsafe(io_base, data)) && timeout != 0) { udelay(100); --timeout; } /* while */ - return err; + return written; } @@ -561,6 +585,30 @@ static int sscape_upload_microcode(struct snd_card *card, int version) } /* + * Restore the SoundScape's MIDI control state after the firmware + * upload has made the host interface available again. + */ +static int sscape_restore_midi_state(struct soundscape *sscape) +{ + bool success; + + guard(spinlock_irqsave)(&sscape->lock); + set_host_mode_unsafe(sscape->io_base); + + success = host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100) && + host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100) && + host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100) && + host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100) && + host_write_ctrl_unsafe(sscape->io_base, CMD_SET_EXTMIDI, 100) && + host_write_ctrl_unsafe(sscape->io_base, 0, 100) && + host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100); + + set_midi_mode_unsafe(sscape->io_base); + + return success ? 0 : -EIO; +} + +/* * Mixer control for the SoundScape's MIDI device. */ static int sscape_midi_info(struct snd_kcontrol *ctl, @@ -661,6 +709,59 @@ static unsigned get_irq_config(int sscape_type, int irq) } /* + * Program the SoundScape's board-specific routing and enable the + * codec path using the resolved IRQ, DMA and joystick settings. + */ +static int sscape_configure_board(struct soundscape *sscape) +{ + unsigned int dma_cfg; + unsigned int irq_cfg; + unsigned int mpu_irq_cfg; + int val; + + irq_cfg = get_irq_config(sscape->type, sscape->irq); + if (irq_cfg == INVALID_IRQ) + return -ENXIO; + + mpu_irq_cfg = get_irq_config(sscape->type, sscape->mpu_irq); + if (mpu_irq_cfg == INVALID_IRQ) + return -ENXIO; + + scoped_guard(spinlock_irqsave, &sscape->lock) { + if (sscape->ic_type == IC_OPUS) + activate_ad1845_unsafe(sscape->io_base); + + sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e); + sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00); + + /* + * Enable and configure the DMA channels ... + */ + sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50); + dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70); + sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg); + sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20); + + mpu_irq_cfg |= mpu_irq_cfg << 2; + val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xf7; + if (sscape->joystick) + val |= 0x08; + sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0xd0); + sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, + 0xf0 | mpu_irq_cfg); + sscape_write_unsafe(sscape->io_base, GA_CDCFG_REG, + 0x09 | DMA_8BIT | + (sscape->dma1 << 4) | (irq_cfg << 1)); + /* + * Enable the master IRQ ... + */ + sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80); + } + + return 0; +} + +/* * Perform certain arcane port-checks to see whether there * is a SoundScape board lurking behind the given ports. */ @@ -890,37 +991,33 @@ _error: /* * Create an ALSA soundcard entry for the SoundScape, using - * the given list of port, IRQ and DMA resources. + * the resolved port, IRQ and DMA resources. */ -static int create_sscape(int dev, struct snd_card *card) +static int create_sscape(struct snd_card *card) { struct soundscape *sscape = get_card_soundscape(card); - unsigned dma_cfg; - unsigned irq_cfg; - unsigned mpu_irq_cfg; struct resource *io_res; struct resource *wss_res; int err; - int val; const char *name; /* * Grab IO ports that we will need to probe so that we * can detect and control this hardware ... */ - io_res = devm_request_region(card->dev, port[dev], 8, "SoundScape"); + io_res = devm_request_region(card->dev, sscape->io_base, 8, "SoundScape"); if (!io_res) { dev_err(card->dev, - "sscape: can't grab port 0x%lx\n", port[dev]); + "sscape: can't grab port 0x%x\n", sscape->io_base); return -EBUSY; } wss_res = NULL; if (sscape->type == SSCAPE_VIVO) { - wss_res = devm_request_region(card->dev, wss_port[dev], 4, + wss_res = devm_request_region(card->dev, sscape->wss_base, 4, "SoundScape"); if (!wss_res) { dev_err(card->dev, "sscape: can't grab port 0x%lx\n", - wss_port[dev]); + sscape->wss_base); return -EBUSY; } } @@ -928,18 +1025,17 @@ static int create_sscape(int dev, struct snd_card *card) /* * Grab one DMA channel ... */ - err = snd_devm_request_dma(card->dev, dma[dev], "SoundScape"); + err = snd_devm_request_dma(card->dev, sscape->dma1, "SoundScape"); if (err < 0) { - dev_err(card->dev, "sscape: can't grab DMA %d\n", dma[dev]); + dev_err(card->dev, "sscape: can't grab DMA %d\n", sscape->dma1); return err; } spin_lock_init(&sscape->lock); sscape->io_res = io_res; sscape->wss_res = wss_res; - sscape->io_base = port[dev]; - if (!detect_sscape(sscape, wss_port[dev])) { + if (!detect_sscape(sscape, sscape->wss_base)) { dev_err(card->dev, "sscape: hardware not detected at 0x%x\n", sscape->io_base); return -ENODEV; @@ -964,66 +1060,28 @@ static int create_sscape(int dev, struct snd_card *card) } dev_info(card->dev, "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n", - name, sscape->io_base, irq[dev], dma[dev]); - - /* - * Check that the user didn't pass us garbage data ... - */ - irq_cfg = get_irq_config(sscape->type, irq[dev]); - if (irq_cfg == INVALID_IRQ) { - dev_err(card->dev, "sscape: Invalid IRQ %d\n", irq[dev]); - return -ENXIO; - } - - mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]); - if (mpu_irq_cfg == INVALID_IRQ) { - dev_err(card->dev, "sscape: Invalid IRQ %d\n", mpu_irq[dev]); - return -ENXIO; - } + name, sscape->io_base, sscape->irq, sscape->dma1); /* * Tell the on-board devices where their resources are (I think - * I can't be sure without a datasheet ... So many magic values!) */ - scoped_guard(spinlock_irqsave, &sscape->lock) { - - sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e); - sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00); - - /* - * Enable and configure the DMA channels ... - */ - sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50); - dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70); - sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg); - sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20); - - mpu_irq_cfg |= mpu_irq_cfg << 2; - val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7; - if (joystick[dev]) - val |= 8; - sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10); - sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg); - sscape_write_unsafe(sscape->io_base, - GA_CDCFG_REG, 0x09 | DMA_8BIT - | (dma[dev] << 4) | (irq_cfg << 1)); - /* - * Enable the master IRQ ... - */ - sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80); - + err = sscape_configure_board(sscape); + if (err < 0) { + dev_err(card->dev, "sscape: Invalid IRQ configuration\n"); + return err; } /* * We have now enabled the codec chip, and so we should * detect the AD1845 device ... */ - err = create_ad1845(card, wss_port[dev], irq[dev], - dma[dev], dma2[dev]); + err = create_ad1845(card, sscape->wss_base, sscape->irq, + sscape->dma1, sscape->dma2); if (err < 0) { dev_err(card->dev, "sscape: No AD1845 device at 0x%lx, IRQ %d\n", - wss_port[dev], irq[dev]); + sscape->wss_base, sscape->irq); return err; } strscpy(card->driver, "SoundScape"); @@ -1040,41 +1098,99 @@ static int create_sscape(int dev, struct snd_card *card) err = sscape_upload_microcode(card, err); if (err == 0) { - err = create_mpu401(card, MIDI_DEVNUM, port[dev], - mpu_irq[dev]); + err = create_mpu401(card, MIDI_DEVNUM, sscape->io_base, + sscape->mpu_irq); if (err < 0) { dev_err(card->dev, "sscape: Failed to create MPU-401 device at 0x%lx\n", - port[dev]); + (unsigned long)sscape->io_base); return err; } - /* - * Initialize mixer - */ - guard(spinlock_irqsave)(&sscape->lock); sscape->midi_vol = 0; - host_write_ctrl_unsafe(sscape->io_base, - CMD_SET_MIDI_VOL, 100); - host_write_ctrl_unsafe(sscape->io_base, - sscape->midi_vol, 100); - host_write_ctrl_unsafe(sscape->io_base, - CMD_XXX_MIDI_VOL, 100); - host_write_ctrl_unsafe(sscape->io_base, - sscape->midi_vol, 100); - host_write_ctrl_unsafe(sscape->io_base, - CMD_SET_EXTMIDI, 100); - host_write_ctrl_unsafe(sscape->io_base, - 0, 100); - host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100); - - set_midi_mode_unsafe(sscape->io_base); + sscape->midi_enabled = true; + err = sscape_restore_midi_state(sscape); + if (err < 0) + dev_warn(card->dev, + "sscape: MIDI init incomplete: %d\n", + err); } } return 0; } +#ifdef CONFIG_PM +/* + * Reload the MIDI firmware and restore the saved MIDI state for + * boards whose MPU-401 side was enabled during probe. + */ +static int sscape_resume_midi(struct snd_card *card) +{ + struct soundscape *sscape = get_card_soundscape(card); + int err, version; + + if (!sscape->midi_enabled) + return 0; + + version = sscape_upload_bootblock(card); + if (version < 0) + return version; + + err = sscape_upload_microcode(card, version); + if (err < 0) + return err; + + outb(0, sscape->io_base); + + return sscape_restore_midi_state(sscape); +} + +/* + * Save the WSS codec state before the SoundScape is suspended. + */ +static int snd_sscape_suspend_card(struct snd_card *card) +{ + struct soundscape *sscape = get_card_soundscape(card); + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + sscape->chip->suspend(sscape->chip); + return 0; +} + +/* + * Restore the board-specific state before resuming the WSS codec. + */ +static int snd_sscape_resume_card(struct snd_card *card) +{ + struct soundscape *sscape = get_card_soundscape(card); + int err; + + err = sscape_configure_board(sscape); + if (err < 0) + return err; + + err = sscape_resume_midi(card); + if (err < 0) + dev_warn(card->dev, "sscape: MIDI restore failed: %d\n", err); + + sscape->chip->resume(sscape->chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} + +static int snd_sscape_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + return snd_sscape_suspend_card(dev_get_drvdata(dev)); +} + +static int snd_sscape_resume(struct device *dev, unsigned int n) +{ + return snd_sscape_resume_card(dev_get_drvdata(dev)); +} +#endif + static int snd_sscape_match(struct device *pdev, unsigned int i) { @@ -1111,8 +1227,9 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev) sscape->type = SSCAPE; dma[dev] &= 0x03; + sscape_store_settings(sscape, dev); - ret = create_sscape(dev, card); + ret = create_sscape(card); if (ret < 0) return ret; @@ -1130,7 +1247,10 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev) static struct isa_driver snd_sscape_driver = { .match = snd_sscape_match, .probe = snd_sscape_probe, - /* FIXME: suspend/resume */ +#ifdef CONFIG_PM + .suspend = snd_sscape_suspend, + .resume = snd_sscape_resume, +#endif .driver = { .name = DEV_NAME }, @@ -1211,8 +1331,9 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard, wss_port[idx] = pnp_port_start(dev, 1); dma2[idx] = pnp_dma(dev, 1); } + sscape_store_settings(sscape, idx); - ret = create_sscape(idx, card); + ret = create_sscape(card); if (ret < 0) return ret; @@ -1227,11 +1348,27 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard, return 0; } +#ifdef CONFIG_PM +static int sscape_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) +{ + return snd_sscape_suspend_card(pnp_get_card_drvdata(pcard)); +} + +static int sscape_pnp_resume(struct pnp_card_link *pcard) +{ + return snd_sscape_resume_card(pnp_get_card_drvdata(pcard)); +} +#endif + static struct pnp_card_driver sscape_pnpc_driver = { .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, .name = "sscape", .id_table = sscape_pnpids, .probe = sscape_pnp_detect, +#ifdef CONFIG_PM + .suspend = sscape_pnp_suspend, + .resume = sscape_pnp_resume, +#endif }; #endif /* CONFIG_PNP */ diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 3a64d0562803..b1c7ed7f1604 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1362,6 +1362,7 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, struct hpi_control *hpi_ctl, char *name) { + int len; char *dir; memset(snd_control, 0, sizeof(*snd_control)); snd_control->name = hpi_ctl->name; @@ -1384,23 +1385,30 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, dir = "Playback "; /* PCM Playback source, or output node */ if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type) - sprintf(hpi_ctl->name, "%s %d %s %d %s%s", - asihpi_src_names[hpi_ctl->src_node_type], - hpi_ctl->src_node_index, - asihpi_dst_names[hpi_ctl->dst_node_type], - hpi_ctl->dst_node_index, - dir, name); + len = snprintf(hpi_ctl->name, sizeof(hpi_ctl->name), + "%s %d %s %d %s%s", + asihpi_src_names[hpi_ctl->src_node_type], + hpi_ctl->src_node_index, + asihpi_dst_names[hpi_ctl->dst_node_type], + hpi_ctl->dst_node_index, + dir, name); else if (hpi_ctl->dst_node_type) { - sprintf(hpi_ctl->name, "%s %d %s%s", - asihpi_dst_names[hpi_ctl->dst_node_type], - hpi_ctl->dst_node_index, - dir, name); + len = snprintf(hpi_ctl->name, sizeof(hpi_ctl->name), + "%s %d %s%s", + asihpi_dst_names[hpi_ctl->dst_node_type], + hpi_ctl->dst_node_index, + dir, name); } else { - sprintf(hpi_ctl->name, "%s %d %s%s", - asihpi_src_names[hpi_ctl->src_node_type], - hpi_ctl->src_node_index, - dir, name); + len = snprintf(hpi_ctl->name, sizeof(hpi_ctl->name), + "%s %d %s%s", + asihpi_src_names[hpi_ctl->src_node_type], + hpi_ctl->src_node_index, + dir, name); } + + if (len >= sizeof(hpi_ctl->name)) + pr_err("asihpi: truncated control name: %s\n", + hpi_ctl->name); } /*------------------------------------------------------------ diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index da2667cb2489..02fe09330939 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -794,7 +794,8 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, struct src *src; int err; int n_amixer = apcm->substream->runtime->channels, i; - unsigned int pitch, rsr = atc->pll_rate; + unsigned int pitch; + unsigned int rsr = atc->pll_rate ? atc->pll_rate : atc->rsr; /* first release old resources */ atc_pcm_release_resources(atc, apcm); @@ -983,6 +984,11 @@ static int atc_select_mic_in(struct ct_atc *atc) return 0; } +static inline enum DAIOTYP atc_spdif_in_type(struct ct_atc *atc) +{ + return (atc->model == CTSB073X) ? SPDIFI_BAY : SPDIFIO; +} + static struct capabilities atc_capabilities(struct ct_atc *atc) { struct hw *hw = atc->hw; @@ -1121,7 +1127,7 @@ static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state) static int atc_spdif_in_unmute(struct ct_atc *atc, unsigned char state) { - return atc_daio_unmute(atc, state, SPDIFIO); + return atc_daio_unmute(atc, state, atc_spdif_in_type(atc)); } static int atc_spdif_out_get_status(struct ct_atc *atc, unsigned int *status) @@ -1404,9 +1410,11 @@ static int atc_get_resources(struct ct_atc *atc) struct sum_desc sum_dsc = {0}; struct sum_mgr *sum_mgr; struct capabilities cap; + int atc_srcs_limit; int err, i; cap = atc->capabilities(atc); + atc_srcs_limit = cap.dedicated_mic ? NUM_ATC_SRCS : 4; atc->daios = kcalloc(NUM_DAIOTYP, sizeof(void *), GFP_KERNEL); if (!atc->daios) @@ -1427,14 +1435,12 @@ static int atc_get_resources(struct ct_atc *atc) daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; da_desc.msr = atc->msr; for (i = 0; i < NUM_DAIOTYP; i++) { - if (((i == MIC) && !cap.dedicated_mic) || - ((i == RCA) && !cap.dedicated_rca) || - i == SPDIFI1) + if (((i == SPDIFIO) && (atc->model == CTSB073X)) || + ((i == SPDIFI_BAY) && (atc->model != CTSB073X)) || + ((i == MIC) && !cap.dedicated_mic) || + ((i == RCA) && !cap.dedicated_rca)) continue; - if (atc->model == CTSB073X && i == SPDIFIO) - da_desc.type = SPDIFI1; - else - da_desc.type = i; + da_desc.type = i; da_desc.output = (i < LINEIM) || (i == RCA); err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); @@ -1450,9 +1456,7 @@ static int atc_get_resources(struct ct_atc *atc) src_dsc.multi = 1; src_dsc.msr = atc->msr; src_dsc.mode = ARCRW; - for (i = 0; i < NUM_ATC_SRCS; i++) { - if (((i > 3) && !cap.dedicated_mic)) - continue; + for (i = 0; i < atc_srcs_limit; i++) { err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&atc->srcs[i]); if (err) @@ -1461,9 +1465,7 @@ static int atc_get_resources(struct ct_atc *atc) srcimp_mgr = atc->rsc_mgrs[SRCIMP]; srcimp_dsc.msr = 8; - for (i = 0; i < NUM_ATC_SRCS; i++) { - if (((i > 3) && !cap.dedicated_mic)) - continue; + for (i = 0; i < atc_srcs_limit; i++) { err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, (struct srcimp **)&atc->srcimps[i]); if (err) @@ -1569,7 +1571,7 @@ static void atc_connect_resources(struct ct_atc *atc) mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc); } - dai = container_of(atc->daios[SPDIFIO], struct dai, daio); + dai = container_of(atc->daios[atc_spdif_in_type(atc)], struct dai, daio); atc_connect_dai(atc->rsc_mgrs[SRC], dai, (struct src **)&atc->srcs[0], (struct srcimp **)&atc->srcimps[0]); diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 4dbb1dd7af32..69aacd06716c 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -35,7 +35,7 @@ static const struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { [LINEIM] = {.left = 0x1b5, .right = 0x1bd}, [SPDIFOO] = {.left = 0x20, .right = 0x21}, [SPDIFIO] = {.left = 0x15, .right = 0x1d}, - [SPDIFI1] = {.left = 0x95, .right = 0x9d}, + [SPDIFI_BAY] = {.left = 0x95, .right = 0x9d}, }; static const struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { @@ -106,7 +106,7 @@ static int daio_device_index(enum DAIOTYP type, struct hw *hw) switch (type) { case SPDIFOO: return 0; case SPDIFIO: return 0; - case SPDIFI1: return 1; + case SPDIFI_BAY: return 1; case LINEO1: return 4; case LINEO2: return 7; case LINEO3: return 5; @@ -120,7 +120,6 @@ static int daio_device_index(enum DAIOTYP type, struct hw *hw) switch (type) { case SPDIFOO: return 0; case SPDIFIO: return 0; - case SPDIFI1: return 1; case LINEO1: return 4; case LINEO2: return 7; case LINEO3: return 5; diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h index ff77d55539a5..c9f6207fe92f 100644 --- a/sound/pci/ctxfi/ctdaio.h +++ b/sound/pci/ctxfi/ctdaio.h @@ -32,7 +32,7 @@ enum DAIOTYP { SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */ MIC, /* Dedicated mic on Titanium HD */ RCA, /* Dedicated RCA on SE-300PCIE */ - SPDIFI1, /* S/PDIF In on internal Drive Bay */ + SPDIFI_BAY, /* S/PDIF In on internal drive bay */ NUM_DAIOTYP }; diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h index da54cbcdb0be..43a0065b40c3 100644 --- a/sound/pci/ctxfi/ctvmem.h +++ b/sound/pci/ctxfi/ctvmem.h @@ -15,7 +15,7 @@ #ifndef CTVMEM_H #define CTVMEM_H -#define CT_PTP_NUM 4 /* num of device page table pages */ +#define CT_PTP_NUM 1 /* num of device page table pages */ #include <linux/mutex.h> #include <linux/list.h> diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index f7c3010624df..53139766fafb 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -122,16 +122,6 @@ config SND_AMD_ACP_CONFIG source "sound/soc/amd/acp/Kconfig" -config SND_SOC_AMD_RPL_ACP6x - tristate "AMD Audio Coprocessor-v6.2 RPL support" - depends on X86 && PCI - help - This option enables Audio Coprocessor i.e. ACP v6.2 support on - AMD RPL platform. By enabling this flag build will be - triggered for ACP PCI driver. - Say m if you have such a device. - If unsure select "N". - config SND_SOC_AMD_ACP63_TOPLEVEL tristate "support for AMD platforms with ACP version >= 6.3" default SND_AMD_ACP_CONFIG diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index 4f89d962cce2..23b25ff0d800 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -17,5 +17,4 @@ obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/ obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/ obj-$(CONFIG_SND_AMD_ACP_CONFIG) += acp/ obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o -obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += rpl/ obj-$(CONFIG_SND_SOC_AMD_PS) += ps/ diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 42aa009c4e13..af559653e625 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -11,7 +11,6 @@ #include <sound/soc-dapm.h> #include <sound/jack.h> #include <linux/clk.h> -#include <linux/gpio.h> #include <linux/module.h> #include <linux/regulator/machine.h> #include <linux/regulator/driver.h> @@ -48,8 +47,6 @@ static struct clk *da7219_dai_bclk; static struct clk *rt5682_dai_wclk; static struct clk *rt5682_dai_bclk; -void *acp_soc_is_rltk_max(struct device *dev); - static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) { int ret; @@ -704,7 +701,7 @@ static struct snd_soc_card cz_rt5682_card = { .num_controls = ARRAY_SIZE(cz_mc_controls), }; -void *acp_soc_is_rltk_max(struct device *dev) +static void *acp_soc_is_rltk_max(struct device *dev) { const struct acpi_device_id *match; diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c index b16dde0e2987..9f3f11256788 100644 --- a/sound/soc/amd/acp-es8336.c +++ b/sound/soc/amd/acp-es8336.c @@ -11,7 +11,6 @@ #include <sound/pcm_params.h> #include <sound/soc-dapm.h> #include <sound/jack.h> -#include <linux/gpio.h> #include <linux/device.h> #include <linux/dmi.h> #include <linux/gpio/consumer.h> diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index c76a4bcc9645..6ad70aa0ea83 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -1252,7 +1252,7 @@ static const struct snd_soc_component_driver acp_asoc_platform = { .pointer = acp_dma_pointer, .delay = acp_dma_delay, .prepare = acp_dma_prepare, - .pcm_construct = acp_dma_new, + .pcm_new = acp_dma_new, }; static int acp_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c index 11d373169380..5c38fa11db7a 100644 --- a/sound/soc/amd/acp-rt5645.c +++ b/sound/soc/amd/acp-rt5645.c @@ -1,29 +1,10 @@ +// SPDX-License-Identifier: MIT /* * Machine driver for AMD ACP Audio engine using Realtek RT5645 codec * * Copyright 2017 Advanced Micro Devices, Inc. * * This file is modified from rt288 machine driver - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * */ #include <sound/core.h> @@ -32,7 +13,6 @@ #include <sound/pcm_params.h> #include <sound/soc-dapm.h> #include <sound/jack.h> -#include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/acpi.h> diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index 88613569fd64..6b1e18b31c1c 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -321,7 +321,7 @@ static const struct snd_soc_component_driver acp_pcm_component = { .close = acp_dma_close, .hw_params = acp_dma_hw_params, .pointer = acp_dma_pointer, - .pcm_construct = acp_dma_new, + .pcm_new = acp_dma_new, .legacy_dai_naming = 1, }; diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index cd7b1acc7216..0f21e5f64531 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -394,10 +394,8 @@ static int create_dmic_dailinks(struct snd_soc_card *card, static int soc_card_dai_links_create(struct snd_soc_card *card) { struct device *dev = card->dev; - struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); int sdw_be_num = 0, dmic_num = 0; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; struct snd_soc_aux_dev *soc_aux; struct snd_soc_codec_conf *codec_conf; struct snd_soc_dai_link *dai_links; @@ -440,7 +438,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) sdw_be_num = ret; /* enable dmic */ - if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num) + if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) dmic_num = 1; dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num); @@ -551,11 +549,11 @@ static int mc_probe(struct platform_device *pdev) " cfg-amp:%d", amp_num); if (!card->components) return -ENOMEM; - if (mach->mach_params.dmic_num) { + if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) { card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s mic:dmic cfg-mics:%d", + "%s mic:acp-dmic cfg-mics:%d", card->components, - mach->mach_params.dmic_num); + 1); if (!card->components) return -ENOMEM; } diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c index 141ea4787d99..46fff565d732 100644 --- a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c +++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c @@ -11,7 +11,6 @@ #include <sound/jack.h> #include <sound/soc-acpi.h> #include <linux/clk.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/i2c.h> diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c index d1eb6f12a183..4affb00bdc95 100644 --- a/sound/soc/amd/acp3x-rt5682-max9836.c +++ b/sound/soc/amd/acp3x-rt5682-max9836.c @@ -11,7 +11,6 @@ #include <sound/soc-dapm.h> #include <sound/jack.h> #include <linux/clk.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/i2c.h> diff --git a/sound/soc/amd/include/acp_2_2_d.h b/sound/soc/amd/include/acp_2_2_d.h index 0118fe9e6a87..d07ec8646ee8 100644 --- a/sound/soc/amd/include/acp_2_2_d.h +++ b/sound/soc/amd/include/acp_2_2_d.h @@ -1,24 +1,8 @@ +/* SPDX-License-Identifier: MIT */ /* * ACP_2_2 Register documentation * * Copyright (C) 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ACP_2_2_D_H diff --git a/sound/soc/amd/include/acp_2_2_enum.h b/sound/soc/amd/include/acp_2_2_enum.h index f3577c851086..26c7993ac395 100644 --- a/sound/soc/amd/include/acp_2_2_enum.h +++ b/sound/soc/amd/include/acp_2_2_enum.h @@ -1,24 +1,8 @@ +/* SPDX-License-Identifier: MIT */ /* * ACP_2_2 Register documentation * * Copyright (C) 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ACP_2_2_ENUM_H diff --git a/sound/soc/amd/include/acp_2_2_sh_mask.h b/sound/soc/amd/include/acp_2_2_sh_mask.h index 32d2d4104309..9749c11e733f 100644 --- a/sound/soc/amd/include/acp_2_2_sh_mask.h +++ b/sound/soc/amd/include/acp_2_2_sh_mask.h @@ -1,24 +1,8 @@ +/* SPDX-License-Identifier: MIT */ /* * ACP_2_2 Register documentation * * Copyright (C) 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ACP_2_2_SH_MASK_H diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c index 7c529fc6ba99..04c014349347 100644 --- a/sound/soc/amd/ps/ps-pdm-dma.c +++ b/sound/soc/amd/ps/ps-pdm-dma.c @@ -351,7 +351,8 @@ static const struct snd_soc_component_driver acp63_pdm_component = { .close = acp63_pdm_dma_close, .hw_params = acp63_pdm_dma_hw_params, .pointer = acp63_pdm_dma_pointer, - .pcm_construct = acp63_pdm_dma_new, + .pcm_new = acp63_pdm_dma_new, + .use_dai_pcm_id = true, }; static int acp63_pdm_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c index 366d7c4bb07e..f27ebbd21379 100644 --- a/sound/soc/amd/ps/ps-sdw-dma.c +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -634,7 +634,7 @@ static const struct snd_soc_component_driver acp63_sdw_component = { .hw_params = acp63_sdw_dma_hw_params, .trigger = acp63_sdw_dma_trigger, .pointer = acp63_sdw_dma_pointer, - .pcm_construct = acp63_sdw_dma_new, + .pcm_new = acp63_sdw_dma_new, .use_dai_pcm_id = true, }; diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 4529404ebd93..37ea5c572eb9 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -363,7 +363,7 @@ static const struct snd_soc_component_driver acp3x_i2s_component = { .close = acp3x_dma_close, .hw_params = acp3x_dma_hw_params, .pointer = acp3x_dma_pointer, - .pcm_construct = acp3x_dma_new, + .pcm_new = acp3x_dma_new, }; static int acp3x_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c index e832c7c4b96f..e60e3821703c 100644 --- a/sound/soc/amd/renoir/acp3x-pdm-dma.c +++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c @@ -376,7 +376,7 @@ static const struct snd_soc_component_driver acp_pdm_component = { .close = acp_pdm_dma_close, .hw_params = acp_pdm_dma_hw_params, .pointer = acp_pdm_dma_pointer, - .pcm_construct = acp_pdm_dma_new, + .pcm_new = acp_pdm_dma_new, .legacy_dai_naming = 1, }; diff --git a/sound/soc/amd/rpl/Makefile b/sound/soc/amd/rpl/Makefile deleted file mode 100644 index a3825c5be4e7..000000000000 --- a/sound/soc/amd/rpl/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ -# RPL platform Support -snd-rpl-pci-acp6x-y := rpl-pci-acp6x.o - -obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += snd-rpl-pci-acp6x.o diff --git a/sound/soc/amd/rpl/rpl-pci-acp6x.c b/sound/soc/amd/rpl/rpl-pci-acp6x.c deleted file mode 100644 index e3afe9172bdf..000000000000 --- a/sound/soc/amd/rpl/rpl-pci-acp6x.c +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * AMD RPL ACP PCI Driver - * - * Copyright 2022 Advanced Micro Devices, Inc. - */ - -#include <linux/pci.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/delay.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> - -#include "rpl_acp6x.h" - -struct rpl_dev_data { - void __iomem *acp6x_base; -}; - -static int rpl_power_on(void __iomem *acp_base) -{ - u32 val; - int timeout; - - val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS); - - if (!val) - return val; - - if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS) - rpl_acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL); - timeout = 0; - while (++timeout < 500) { - val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS); - if (!val) - return 0; - udelay(1); - } - return -ETIMEDOUT; -} - -static int rpl_reset(void __iomem *acp_base) -{ - u32 val; - int timeout; - - rpl_acp_writel(1, acp_base + ACP_SOFT_RESET); - timeout = 0; - while (++timeout < 500) { - val = rpl_acp_readl(acp_base + ACP_SOFT_RESET); - if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK) - break; - cpu_relax(); - } - rpl_acp_writel(0, acp_base + ACP_SOFT_RESET); - timeout = 0; - while (++timeout < 500) { - val = rpl_acp_readl(acp_base + ACP_SOFT_RESET); - if (!val) - return 0; - cpu_relax(); - } - return -ETIMEDOUT; -} - -static int rpl_init(void __iomem *acp_base) -{ - int ret; - - /* power on */ - ret = rpl_power_on(acp_base); - if (ret) { - pr_err("ACP power on failed\n"); - return ret; - } - rpl_acp_writel(0x01, acp_base + ACP_CONTROL); - /* Reset */ - ret = rpl_reset(acp_base); - if (ret) { - pr_err("ACP reset failed\n"); - return ret; - } - rpl_acp_writel(0x03, acp_base + ACP_CLKMUX_SEL); - return 0; -} - -static int rpl_deinit(void __iomem *acp_base) -{ - int ret; - - /* Reset */ - ret = rpl_reset(acp_base); - if (ret) { - pr_err("ACP reset failed\n"); - return ret; - } - rpl_acp_writel(0x00, acp_base + ACP_CLKMUX_SEL); - rpl_acp_writel(0x00, acp_base + ACP_CONTROL); - return 0; -} - -static int snd_rpl_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) -{ - struct rpl_dev_data *adata; - u32 addr; - int ret; - - /* RPL device check */ - switch (pci->revision) { - case 0x62: - break; - default: - dev_dbg(&pci->dev, "acp6x pci device not found\n"); - return -ENODEV; - } - if (pci_enable_device(pci)) { - dev_err(&pci->dev, "pci_enable_device failed\n"); - return -ENODEV; - } - - ret = pci_request_regions(pci, "AMD ACP6x audio"); - if (ret < 0) { - dev_err(&pci->dev, "pci_request_regions failed\n"); - goto disable_pci; - } - - adata = devm_kzalloc(&pci->dev, sizeof(struct rpl_dev_data), - GFP_KERNEL); - if (!adata) { - ret = -ENOMEM; - goto release_regions; - } - - addr = pci_resource_start(pci, 0); - adata->acp6x_base = devm_ioremap(&pci->dev, addr, - pci_resource_len(pci, 0)); - if (!adata->acp6x_base) { - ret = -ENOMEM; - goto release_regions; - } - pci_set_master(pci); - pci_set_drvdata(pci, adata); - ret = rpl_init(adata->acp6x_base); - if (ret) - goto release_regions; - pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS); - pm_runtime_use_autosuspend(&pci->dev); - pm_runtime_put_noidle(&pci->dev); - pm_runtime_allow(&pci->dev); - - return 0; -release_regions: - pci_release_regions(pci); -disable_pci: - pci_disable_device(pci); - - return ret; -} - -static int snd_rpl_suspend(struct device *dev) -{ - struct rpl_dev_data *adata; - int ret; - - adata = dev_get_drvdata(dev); - ret = rpl_deinit(adata->acp6x_base); - if (ret) - dev_err(dev, "ACP de-init failed\n"); - return ret; -} - -static int snd_rpl_resume(struct device *dev) -{ - struct rpl_dev_data *adata; - int ret; - - adata = dev_get_drvdata(dev); - ret = rpl_init(adata->acp6x_base); - if (ret) - dev_err(dev, "ACP init failed\n"); - return ret; -} - -static const struct dev_pm_ops rpl_pm = { - RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL) - SYSTEM_SLEEP_PM_OPS(snd_rpl_suspend, snd_rpl_resume) -}; - -static void snd_rpl_remove(struct pci_dev *pci) -{ - struct rpl_dev_data *adata; - int ret; - - adata = pci_get_drvdata(pci); - ret = rpl_deinit(adata->acp6x_base); - if (ret) - dev_err(&pci->dev, "ACP de-init failed\n"); - pm_runtime_forbid(&pci->dev); - pm_runtime_get_noresume(&pci->dev); - pci_release_regions(pci); - pci_disable_device(pci); -} - -static const struct pci_device_id snd_rpl_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID), - .class = PCI_CLASS_MULTIMEDIA_OTHER << 8, - .class_mask = 0xffffff }, - { 0, }, -}; -MODULE_DEVICE_TABLE(pci, snd_rpl_ids); - -static struct pci_driver rpl_acp6x_driver = { - .name = KBUILD_MODNAME, - .id_table = snd_rpl_ids, - .probe = snd_rpl_probe, - .remove = snd_rpl_remove, - .driver = { - .pm = pm_ptr(&rpl_pm), - } -}; - -module_pci_driver(rpl_acp6x_driver); - -MODULE_DESCRIPTION("AMD ACP RPL PCI driver"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/rpl/rpl_acp6x.h b/sound/soc/amd/rpl/rpl_acp6x.h deleted file mode 100644 index f5816a33632e..000000000000 --- a/sound/soc/amd/rpl/rpl_acp6x.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * AMD ACP Driver - * - * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved. - */ - -#include "rpl_acp6x_chip_offset_byte.h" - -#define ACP_DEVICE_ID 0x15E2 -#define ACP6x_PHY_BASE_ADDRESS 0x1240000 - -#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 -#define ACP_PGFSM_CNTL_POWER_ON_MASK 1 -#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0 -#define ACP_PGFSM_STATUS_MASK 3 -#define ACP_POWERED_ON 0 -#define ACP_POWER_ON_IN_PROGRESS 1 -#define ACP_POWERED_OFF 2 -#define ACP_POWER_OFF_IN_PROGRESS 3 - -#define DELAY_US 5 -#define ACP_COUNTER 20000 - -/* time in ms for runtime suspend delay */ -#define ACP_SUSPEND_DELAY_MS 2000 - -static inline u32 rpl_acp_readl(void __iomem *base_addr) -{ - return readl(base_addr - ACP6x_PHY_BASE_ADDRESS); -} - -static inline void rpl_acp_writel(u32 val, void __iomem *base_addr) -{ - writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS); -} diff --git a/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h b/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h deleted file mode 100644 index 456498f5396d..000000000000 --- a/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * AMD ACP 6.2 Register Documentation - * - * Copyright 2022 Advanced Micro Devices, Inc. - */ - -#ifndef _rpl_acp6x_OFFSET_HEADER -#define _rpl_acp6x_OFFSET_HEADER - -/* Registers from ACP_CLKRST block */ -#define ACP_SOFT_RESET 0x1241000 -#define ACP_CONTROL 0x1241004 -#define ACP_STATUS 0x1241008 -#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010 -#define ACP_PGFSM_CONTROL 0x124101C -#define ACP_PGFSM_STATUS 0x1241020 -#define ACP_CLKMUX_SEL 0x1241024 - -/* Registers from ACP_AON block */ -#define ACP_PME_EN 0x1241400 -#define ACP_DEVICE_STATE 0x1241404 -#define AZ_DEVICE_STATE 0x1241408 -#define ACP_PIN_CONFIG 0x1241440 -#define ACP_PAD_PULLUP_CTRL 0x1241444 -#define ACP_PAD_PULLDOWN_CTRL 0x1241448 -#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124144C -#define ACP_PAD_SCHMEN_CTRL 0x1241450 - -#endif diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c index 6ce82cd8859b..831e30e9b042 100644 --- a/sound/soc/amd/vangogh/acp5x-pcm-dma.c +++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c @@ -357,7 +357,7 @@ static const struct snd_soc_component_driver acp5x_i2s_component = { .close = acp5x_dma_close, .hw_params = acp5x_dma_hw_params, .pointer = acp5x_dma_pointer, - .pcm_construct = acp5x_dma_new, + .pcm_new = acp5x_dma_new, }; static int acp5x_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index aa6200933182..c5cf45881416 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -48,6 +48,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { { .driver_data = &acp6x_card, .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Lecoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "Bellator N176"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "HP"), DMI_MATCH(DMI_PRODUCT_NAME, "HP Laptop 15-fc0xxx"), } @@ -496,6 +503,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { { .driver_data = &acp6x_card, .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vector A16 HX A8WHG"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"), DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"), } @@ -734,6 +748,20 @@ 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_BOARD_NAME, "BM1403CDA"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7VF"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7VE"), } diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c index 1c8aad849916..710db721ffa4 100644 --- a/sound/soc/amd/yc/acp6x-pdm-dma.c +++ b/sound/soc/amd/yc/acp6x-pdm-dma.c @@ -346,7 +346,7 @@ static const struct snd_soc_component_driver acp6x_pdm_component = { .close = acp6x_pdm_dma_close, .hw_params = acp6x_pdm_dma_hw_params, .pointer = acp6x_pdm_dma_pointer, - .pcm_construct = acp6x_pdm_dma_new, + .pcm_new = acp6x_pdm_dma_new, .legacy_dai_naming = 1, }; diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c index 1140ed1cbb3d..671443204e04 100644 --- a/sound/soc/amd/yc/pci-acp6x.c +++ b/sound/soc/amd/yc/pci-acp6x.c @@ -163,6 +163,7 @@ static int snd_acp6x_probe(struct pci_dev *pci, switch (pci->revision) { case 0x60: case 0x6f: + case 0x62: /* RPL */ break; default: dev_dbg(&pci->dev, "acp6x pci device not found\n"); @@ -208,6 +209,17 @@ static int snd_acp6x_probe(struct pci_dev *pci, case ACP_CONFIG_15: dev_info(&pci->dev, "Audio Mode %d\n", val); break; + case ACP_CONFIG_10: + case ACP_CONFIG_11: + case ACP_CONFIG_12: + case ACP_CONFIG_13: + case ACP_CONFIG_14: + /* PIN 10 to 14 is reserve for RPL */ + if (pci->revision == 0x62) { + dev_info(&pci->dev, "RPL Audio Mode %d\n", val); + break; + } + fallthrough; default: adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig index d8dc2f1ccc83..f1d071a99fbf 100644 --- a/sound/soc/apple/Kconfig +++ b/sound/soc/apple/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menu "Apple" config SND_SOC_APPLE_MCA diff --git a/sound/soc/apple/Makefile b/sound/soc/apple/Makefile index 1eb8fbef60c6..252ba5fc18cb 100644 --- a/sound/soc/apple/Makefile +++ b/sound/soc/apple/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only snd-soc-apple-mca-y := mca.o obj-$(CONFIG_SND_SOC_APPLE_MCA) += snd-soc-apple-mca.o diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index c4dcb2b54591..39269cc7def6 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -1002,8 +1002,8 @@ static const struct snd_soc_component_driver mca_component = { .hw_params = mca_hw_params, .trigger = mca_trigger, .pointer = mca_pointer, - .pcm_construct = mca_pcm_new, - .pcm_destruct = mca_pcm_free, + .pcm_new = mca_pcm_new, + .pcm_free = mca_pcm_free, }; static void apple_mca_release(struct mca_data *mca) diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index 1a0c584801f0..374f93fa6e0b 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -316,7 +316,7 @@ static const struct snd_soc_component_driver atmel_soc_platform = { .prepare = atmel_pcm_prepare, .trigger = atmel_pcm_trigger, .pointer = atmel_pcm_pointer, - .pcm_construct = atmel_pcm_new, + .pcm_new = atmel_pcm_new, }; int atmel_pcm_pdc_platform_register(struct device *dev) diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 3392693faeb9..3d10d6460472 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -319,7 +319,7 @@ static const struct snd_soc_component_driver au1xpsc_soc_component = { .prepare = au1xpsc_pcm_prepare, .trigger = au1xpsc_pcm_trigger, .pointer = au1xpsc_pcm_pointer, - .pcm_construct = au1xpsc_pcm_new, + .pcm_new = au1xpsc_pcm_new, }; static int au1xpsc_pcm_drvprobe(struct platform_device *pdev) diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c index 8d91a0e0f112..026d9f36db10 100644 --- a/sound/soc/au1x/dma.c +++ b/sound/soc/au1x/dma.c @@ -296,7 +296,7 @@ static const struct snd_soc_component_driver alchemy_pcm_soc_component = { .hw_free = alchemy_pcm_hw_free, .trigger = alchemy_pcm_trigger, .pointer = alchemy_pcm_pointer, - .pcm_construct = alchemy_pcm_new, + .pcm_new = alchemy_pcm_new, }; static int alchemy_pcm_drvprobe(struct platform_device *pdev) diff --git a/sound/soc/bcm/bcm63xx-pcm-whistler.c b/sound/soc/bcm/bcm63xx-pcm-whistler.c index 6a3fd0d89365..a40f6bed69c6 100644 --- a/sound/soc/bcm/bcm63xx-pcm-whistler.c +++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c @@ -386,7 +386,7 @@ static const struct snd_soc_component_driver bcm63xx_soc_platform = { .prepare = bcm63xx_pcm_prepare, .trigger = bcm63xx_pcm_trigger, .pointer = bcm63xx_pcm_pointer, - .pcm_construct = bcm63xx_soc_pcm_new, + .pcm_new = bcm63xx_soc_pcm_new, }; int bcm63xx_soc_platform_probe(struct platform_device *pdev, diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c index 4cb2fe10bcdc..dc243f2dfd32 100644 --- a/sound/soc/bcm/cygnus-pcm.c +++ b/sound/soc/bcm/cygnus-pcm.c @@ -713,7 +713,7 @@ static const struct snd_soc_component_driver cygnus_soc_platform = { .prepare = cygnus_pcm_prepare, .trigger = cygnus_pcm_trigger, .pointer = cygnus_pcm_pointer, - .pcm_construct = cygnus_dma_new, + .pcm_new = cygnus_dma_new, }; int cygnus_soc_platform_register(struct device *dev, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index adb3fb923be3..cf94a1c756e0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -398,31 +398,21 @@ config SND_SOC_WM_HUBS default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m config SND_SOC_WM_ADSP - tristate + tristate "Cirrus Logic wm_adsp driver" if KUNIT select FW_CS_DSP select SND_SOC_COMPRESS - default y if SND_SOC_MADERA=y - default y if SND_SOC_CS47L24=y - default y if SND_SOC_WM5102=y - default y if SND_SOC_WM5110=y - default y if SND_SOC_WM2200=y - default y if SND_SOC_CS35L41_SPI=y - default y if SND_SOC_CS35L41_I2C=y - default y if SND_SOC_CS35L45_SPI=y - default y if SND_SOC_CS35L45_I2C=y - default y if SND_SOC_CS35L56=y - default y if SND_SOC_CS48L32=y - default m if SND_SOC_MADERA=m - default m if SND_SOC_CS47L24=m - default m if SND_SOC_WM5102=m - default m if SND_SOC_WM5110=m - default m if SND_SOC_WM2200=m - default m if SND_SOC_CS35L41_SPI=m - default m if SND_SOC_CS35L41_I2C=m - default m if SND_SOC_CS35L45_SPI=m - default m if SND_SOC_CS35L45_I2C=m - default m if SND_SOC_CS35L56=m - default m if SND_SOC_CS48L32=m + +config SND_SOC_WM_ADSP_TEST + tristate "KUnit tests for Cirrus Logic wm_adsp" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on SND_SOC_WM_ADSP + default KUNIT_ALL_TESTS + help + This builds KUnit tests for the Cirrus Logic wm_adsp library. + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + If in doubt, say "N". config SND_SOC_AB8500_CODEC tristate @@ -822,6 +812,7 @@ config SND_SOC_CS35L41_LIB config SND_SOC_CS35L41 tristate + select SND_SOC_WM_ADSP config SND_SOC_CS35L41_SPI tristate "Cirrus Logic CS35L41 CODEC (SPI)" @@ -840,6 +831,7 @@ config SND_SOC_CS35L41_I2C config SND_SOC_CS35L45 tristate select REGMAP_IRQ + select SND_SOC_WM_ADSP config SND_SOC_CS35L45_SPI tristate "Cirrus Logic CS35L45 CODEC (SPI)" @@ -863,6 +855,7 @@ config SND_SOC_CS35L45_I2C config SND_SOC_CS35L56 tristate + select SND_SOC_WM_ADSP config SND_SOC_CS35L56_SHARED select SND_SOC_CS_AMP_LIB @@ -928,6 +921,19 @@ config SND_SOC_CS35L56_CAL_SET_CTRL If unsure select "N". +config SND_SOC_CS35L56_CAL_PERFORM_CTRL + bool "CS35L56 ALSA control to perform factory calibration" + default N + select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON + help + Allow performing factory calibration data through an ALSA + control. It is recommended to use the debugfs method instead + because debugfs has restricted access permissions. + + On most platforms this is not needed. + + If unsure select "N". + config SND_SOC_CS35L56_TEST tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS depends on SND_SOC_CS35L56 && KUNIT @@ -1100,6 +1106,7 @@ config SND_SOC_CS47L15 config SND_SOC_CS47L24 tristate depends on MFD_CS47L24 && MFD_ARIZONA + select SND_SOC_WM_ADSP config SND_SOC_CS47L35 tristate @@ -1121,6 +1128,7 @@ config SND_SOC_CS48L32 tristate "Cirrus Logic CS48L32 audio DSP" depends on SPI_MASTER select REGMAP_SPI + select SND_SOC_WM_ADSP help Build the codec driver for the Cirrus Logic CS48L32 audio DSP. @@ -1379,6 +1387,7 @@ config SND_SOC_LOCHNAGAR_SC config SND_SOC_MADERA tristate + select SND_SOC_WM_ADSP default y if SND_SOC_CS47L15=y default y if SND_SOC_CS47L35=y default y if SND_SOC_CS47L85=y @@ -2364,9 +2373,11 @@ config SND_SOC_UDA1342 mic inputs), stereo audio DAC, with basic audio processing. config SND_SOC_UDA1380 - tristate + tristate "Philips UDA1380 CODEC" depends on I2C - depends on GPIOLIB_LEGACY + help + The UDA1380 codec is used in the HTC Magician and on a number of + Samsung reference boards, as well as the LPC32xx series. config SND_SOC_WCD_CLASSH tristate @@ -2477,6 +2488,7 @@ config SND_SOC_WM2000 config SND_SOC_WM2200 tristate depends on I2C + select SND_SOC_WM_ADSP config SND_SOC_WM5100 tristate @@ -2485,10 +2497,12 @@ config SND_SOC_WM5100 config SND_SOC_WM5102 tristate depends on MFD_WM5102 && MFD_ARIZONA + select SND_SOC_WM_ADSP config SND_SOC_WM5110 tristate depends on MFD_WM5110 && MFD_ARIZONA + select SND_SOC_WM_ADSP config SND_SOC_WM8350 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3ddee5298721..172861d17cfd 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -361,6 +361,7 @@ snd-soc-wcd938x-sdw-y := wcd938x-sdw.o snd-soc-wcd939x-y := wcd939x.o snd-soc-wcd939x-sdw-y := wcd939x-sdw.o snd-soc-wm-adsp-y := wm_adsp.o +snd-soc-wm-adsp-test-y := wm_adsp_fw_find_test.o snd-soc-wm0010-y := wm0010.o snd-soc-wm1250-ev1-y := wm1250-ev1.o snd-soc-wm2000-y := wm2000.o @@ -862,6 +863,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o +obj-$(CONFIG_SND_SOC_WM_ADSP_TEST) += snd-soc-wm-adsp-test.o 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 diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 73684fc5beb1..691db6565c20 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -38,7 +38,6 @@ static const char *ak5558_supply_names[AK5558_NUM_SUPPLIES] = { /* AK5558 Codec Private Data */ struct ak5558_priv { struct regulator_bulk_data supplies[AK5558_NUM_SUPPLIES]; - struct snd_soc_component component; struct regmap *regmap; struct i2c_client *i2c; struct gpio_desc *reset_gpiod; /* Reset & Power down GPIO */ diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c index 303c1d44ebd8..a9063bac2752 100644 --- a/sound/soc/codecs/arizona-jack.c +++ b/sound/soc/codecs/arizona-jack.c @@ -11,7 +11,6 @@ #include <linux/interrupt.h> #include <linux/err.h> #include <linux/gpio/consumer.h> -#include <linux/gpio.h> #include <linux/input.h> #include <linux/pm_runtime.h> #include <linux/property.h> @@ -459,11 +458,6 @@ static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading, bool *mic) { struct arizona *arizona = info->arizona; -#ifdef CONFIG_GPIOLIB_LEGACY - int id_gpio = arizona->pdata.hpdet_id_gpio; -#else - int id_gpio = 0; -#endif if (!arizona->pdata.hpdet_acc_id) return 0; @@ -474,9 +468,8 @@ static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading, */ info->hpdet_res[info->num_hpdet_res++] = *reading; -#ifdef CONFIG_GPIOLIB_LEGACY /* Only check the mic directly if we didn't already ID it */ - if (id_gpio && info->num_hpdet_res == 1) { + if (info->hpdet_id_gpio && info->num_hpdet_res == 1) { dev_dbg(arizona->dev, "Measuring mic\n"); regmap_update_bits(arizona->regmap, @@ -486,13 +479,12 @@ static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading, ARIZONA_ACCDET_MODE_HPR | info->micd_modes[0].src); - gpio_set_value_cansleep(id_gpio, 1); + gpiod_set_value_cansleep(info->hpdet_id_gpio, 1); regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, ARIZONA_HP_POLL, ARIZONA_HP_POLL); return -EAGAIN; } -#endif /* OK, got both. Now, compare... */ dev_dbg(arizona->dev, "HPDET measured %d %d\n", @@ -514,7 +506,7 @@ static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading, /* * If we measure the mic as high impedance */ - if (!id_gpio || info->hpdet_res[1] > 50) { + if (!info->hpdet_id_gpio || info->hpdet_res[1] > 50) { dev_dbg(arizona->dev, "Detected mic\n"); *mic = true; info->detecting = true; @@ -533,9 +525,6 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) { struct arizona_priv *info = data; struct arizona *arizona = info->arizona; -#ifdef CONFIG_GPIOLIB_LEGACY - int id_gpio = arizona->pdata.hpdet_id_gpio; -#endif int ret, reading, state, report; bool mic = false; @@ -591,10 +580,8 @@ done: arizona_extcon_hp_clamp(info, false); -#ifdef CONFIG_GPIOLIB_LEGACY - if (id_gpio) - gpio_set_value_cansleep(id_gpio, 0); -#endif + if (info->hpdet_id_gpio) + gpiod_set_value_cansleep(info->hpdet_id_gpio, 0); /* If we have a mic then reenable MICDET */ if (state && (mic || info->mic)) @@ -1325,58 +1312,33 @@ int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev) regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1, ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw); -#ifdef CONFIG_GPIOLIB_LEGACY - if (pdata->micd_pol_gpio > 0) { - if (info->micd_modes[0].gpio) - mode = GPIOF_OUT_INIT_HIGH; - else - mode = GPIOF_OUT_INIT_LOW; - - ret = devm_gpio_request_one(dev, pdata->micd_pol_gpio, - mode, "MICD polarity"); - if (ret != 0) { - dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", - pdata->micd_pol_gpio, ret); - return ret; - } - - info->micd_pol_gpio = gpio_to_desc(pdata->micd_pol_gpio); - } else -#endif - { - if (info->micd_modes[0].gpio) - mode = GPIOD_OUT_HIGH; - else - mode = GPIOD_OUT_LOW; + if (info->micd_modes[0].gpio) + mode = GPIOD_OUT_HIGH; + else + mode = GPIOD_OUT_LOW; - /* We can't use devm here because we need to do the get - * against the MFD device, as that is where the of_node - * will reside, but if we devm against that the GPIO - * will not be freed if the extcon driver is unloaded. - */ - info->micd_pol_gpio = gpiod_get_optional(arizona->dev, - "wlf,micd-pol", - mode); - if (IS_ERR(info->micd_pol_gpio)) { - ret = PTR_ERR(info->micd_pol_gpio); - dev_err_probe(arizona->dev, ret, "getting microphone polarity GPIO\n"); - return ret; - } + /* We can't use devm here because we need to do the get + * against the MFD device, as that is where the of_node + * will reside, but if we devm against that the GPIO + * will not be freed if the extcon driver is unloaded. + */ + info->micd_pol_gpio = gpiod_get_optional(arizona->dev, + "wlf,micd-pol", + mode); + if (IS_ERR(info->micd_pol_gpio)) { + ret = PTR_ERR(info->micd_pol_gpio); + dev_err_probe(arizona->dev, ret, "getting microphone polarity GPIO\n"); + return ret; } -#ifdef CONFIG_GPIOLIB_LEGACY - if (arizona->pdata.hpdet_id_gpio > 0) { - ret = devm_gpio_request_one(dev, arizona->pdata.hpdet_id_gpio, - GPIOF_OUT_INIT_LOW, - "HPDET"); - if (ret != 0) { - dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", - arizona->pdata.hpdet_id_gpio, ret); - gpiod_put(info->micd_pol_gpio); - return ret; - } + info->hpdet_id_gpio = gpiod_get_optional(arizona->dev, + "wlf,hpdet-id-gpio", + mode); + if (IS_ERR(info->hpdet_id_gpio)) { + ret = PTR_ERR(info->hpdet_id_gpio); + dev_err_probe(arizona->dev, ret, "getting headphone detect ID GPIO\n"); + return ret; } -#endif return 0; } @@ -1385,6 +1347,7 @@ EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_probe); int arizona_jack_codec_dev_remove(struct arizona_priv *info) { gpiod_put(info->micd_pol_gpio); + gpiod_put(info->hpdet_id_gpio); return 0; } EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_remove); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index ecd8890eefc1..0703182d87b3 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -100,6 +100,7 @@ struct arizona_priv { struct snd_soc_jack *jack; struct regulator *micvdd; struct gpio_desc *micd_pol_gpio; + struct gpio_desc *hpdet_id_gpio; u16 last_jackdet; diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c index 613daccca3af..37ca42a25889 100644 --- a/sound/soc/codecs/aw87390.c +++ b/sound/soc/codecs/aw87390.c @@ -544,8 +544,7 @@ static int aw87390_i2c_probe(struct i2c_client *i2c) const struct snd_soc_component_driver *priv; int ret; - ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); - if (!ret) + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n"); aw87390 = devm_kzalloc(&i2c->dev, sizeof(*aw87390), GFP_KERNEL); diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c index fbd1fd12381a..8c5bb3ea0227 100644 --- a/sound/soc/codecs/aw88081.c +++ b/sound/soc/codecs/aw88081.c @@ -1253,8 +1253,7 @@ static int aw88081_i2c_probe(struct i2c_client *i2c) struct aw88081 *aw88081; int ret; - ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); - if (!ret) + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed"); aw88081 = devm_kzalloc(&i2c->dev, sizeof(*aw88081), GFP_KERNEL); diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c index daee4de9e3b0..ea277a940c44 100644 --- a/sound/soc/codecs/aw88166.c +++ b/sound/soc/codecs/aw88166.c @@ -1574,18 +1574,22 @@ 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 char *fw_name; int ret; aw88166->aw_pa->fw_status = AW88166_DEV_FW_FAILED; - ret = request_firmware(&cont, AW88166_ACF_FILE, aw88166->aw_pa->dev); + if (device_property_read_string(aw88166->aw_pa->dev, "firmware-name", &fw_name) < 0) + fw_name = AW88166_ACF_FILE; + + ret = request_firmware(&cont, fw_name, aw88166->aw_pa->dev); if (ret) { - dev_err(aw88166->aw_pa->dev, "request [%s] failed!\n", AW88166_ACF_FILE); + dev_err(aw88166->aw_pa->dev, "request [%s] failed!\n", fw_name); return ret; } dev_dbg(aw88166->aw_pa->dev, "loaded %s - size: %zu\n", - AW88166_ACF_FILE, cont ? cont->size : 0); + fw_name, cont ? cont->size : 0); aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev, struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL); @@ -1599,7 +1603,7 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg); if (ret) { - dev_err(aw88166->aw_pa->dev, "load [%s] failed!\n", AW88166_ACF_FILE); + dev_err(aw88166->aw_pa->dev, "load [%s] failed!\n", fw_name); return ret; } diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index 43c03d3cb252..a6805d5405cd 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -1094,17 +1094,22 @@ 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 char *fw_name; int ret; aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED; - ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev); + ret = device_property_read_string(aw88261->aw_pa->dev, "firmware-name", &fw_name); + if (ret) + fw_name = AW88261_ACF_FILE; + + ret = request_firmware(&cont, fw_name, aw88261->aw_pa->dev); if (ret) return dev_err_probe(aw88261->aw_pa->dev, ret, - "load [%s] failed!", AW88261_ACF_FILE); + "load [%s] failed!", fw_name); dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n", - AW88261_ACF_FILE, cont ? cont->size : 0); + fw_name, cont ? cont->size : 0); aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); if (!aw88261->aw_cfg) { @@ -1117,7 +1122,7 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg); if (ret) { - dev_err(aw88261->aw_pa->dev, "load [%s] failed !", AW88261_ACF_FILE); + dev_err(aw88261->aw_pa->dev, "load [%s] failed !", fw_name); return ret; } @@ -1237,8 +1242,7 @@ static int aw88261_i2c_probe(struct i2c_client *i2c) struct aw88261 *aw88261; int ret; - ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); - if (!ret) + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed"); aw88261 = devm_kzalloc(&i2c->dev, sizeof(*aw88261), GFP_KERNEL); diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index f78a85b89d95..7dc5a7c3ca96 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -948,7 +948,7 @@ static const struct snd_soc_component_driver wov_component_driver = { .hw_params = wov_pcm_hw_params, .hw_free = wov_pcm_hw_free, .pointer = wov_pcm_pointer, - .pcm_construct = wov_pcm_new, + .pcm_new = wov_pcm_new, }; static int cros_ec_codec_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c index 2b1529343f11..636d0f3198b8 100644 --- a/sound/soc/codecs/cs-amp-lib-test.c +++ b/sound/soc/codecs/cs-amp-lib-test.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/overflow.h> #include <linux/pci_ids.h> -#include <linux/platform_device.h> #include <linux/random.h> #include <sound/cs-amp-lib.h> diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 8b131975143d..b34b1f5f121f 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -716,6 +716,8 @@ int cs_amp_get_vendor_spkid(struct device *dev) { int i, ret; + KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_vendor_spkid, dev); + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) return -ENOENT; diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index ee56dfceedeb..b2a076706c79 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1404,6 +1404,7 @@ static int cs35l41_runtime_suspend(struct device *dev) if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) return 0; + wm_adsp_hibernate(&cs35l41->dsp, true); cs35l41_enter_hibernate(dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type); regcache_cache_only(cs35l41->regmap, true); @@ -1432,10 +1433,14 @@ static int cs35l41_runtime_resume(struct device *dev) cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); ret = regcache_sync(cs35l41->regmap); cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + + wm_adsp_hibernate(&cs35l41->dsp, false); + if (ret) { dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret); return ret; } + cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg); return 0; diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index 7aa558d6362f..a032bb23b4ac 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -984,6 +984,7 @@ static int cs35l45_runtime_suspend(struct device *dev) if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running) return 0; + wm_adsp_hibernate(&cs35l45->dsp, true); cs35l45_enter_hibernate(cs35l45); regcache_cache_only(cs35l45->regmap, true); @@ -1014,6 +1015,8 @@ static int cs35l45_runtime_resume(struct device *dev) if (ret != 0) dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret); + wm_adsp_hibernate(&cs35l45->dsp, false); + /* Clear global error status */ regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 30b3192d6ce9..9dc47fec1ea0 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -14,6 +14,7 @@ #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_registers.h> #include <linux/soundwire/sdw_type.h> +#include <linux/string_choices.h> #include <linux/swab.h> #include <linux/types.h> #include <linux/workqueue.h> @@ -340,6 +341,14 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); struct sdw_slave_prop *prop = &peripheral->prop; struct sdw_dpn_prop *ports; + u8 clock_stop_1 = false; + int ret; + + ret = fwnode_property_read_u8(dev_fwnode(cs35l56->base.dev), + "mipi-sdw-clock-stop-mode1-supported", + &clock_stop_1); + if (ret == 0) + prop->clk_stop_mode1 = !!clock_stop_1; ports = devm_kcalloc(cs35l56->base.dev, 2, sizeof(*ports), GFP_KERNEL); if (!ports) @@ -363,6 +372,9 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) ports[1].ch_prep_timeout = 10; prop->src_dpn_prop = &ports[1]; + dev_dbg(&peripheral->dev, "clock stop mode 1 supported: %s\n", + str_yes_no(prop->clk_stop_mode1)); + return 0; } @@ -374,6 +386,7 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, switch (status) { case SDW_SLAVE_ATTACHED: dev_dbg(cs35l56->base.dev, "%s: ATTACHED\n", __func__); + cs35l56->sdw_in_clock_stop_1 = false; if (cs35l56->sdw_attached) break; @@ -399,25 +412,35 @@ static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral, { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - dev_dbg(cs35l56->base.dev, "%s: mode:%d type:%d\n", __func__, mode, type); + dev_dbg(cs35l56->base.dev, "%s: clock_stop_mode%d stage:%d\n", __func__, mode, type); - return 0; + switch (type) { + case SDW_CLK_POST_PREPARE: + if (mode == SDW_CLK_STOP_MODE1) + cs35l56->sdw_in_clock_stop_1 = true; + else + cs35l56->sdw_in_clock_stop_1 = false; + return 0; + default: + return 0; + } } static const struct sdw_slave_ops cs35l56_sdw_ops = { .read_prop = cs35l56_sdw_read_prop, .interrupt_callback = cs35l56_sdw_interrupt, .update_status = cs35l56_sdw_update_status, -#ifdef DEBUG .clk_stop = cs35l56_sdw_clk_stop, -#endif }; static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56) { struct sdw_slave *peripheral = cs35l56->sdw_peripheral; - if (peripheral->unattach_request) { + dev_dbg(cs35l56->base.dev, "attached:%u unattach_request:%u in_clock_stop_1:%u\n", + cs35l56->sdw_attached, peripheral->unattach_request, cs35l56->sdw_in_clock_stop_1); + + if (cs35l56->sdw_in_clock_stop_1 || peripheral->unattach_request) { /* Cannot access registers until bus is re-initialized. */ dev_dbg(cs35l56->base.dev, "Wait for initialization_complete\n"); if (!wait_for_completion_timeout(&peripheral->initialization_complete, @@ -427,6 +450,7 @@ static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs } peripheral->unattach_request = 0; + cs35l56->sdw_in_clock_stop_1 = false; /* * Don't call regcache_mark_dirty(), we can't be sure that the diff --git a/sound/soc/codecs/cs35l56-shared-test.c b/sound/soc/codecs/cs35l56-shared-test.c index 94db02aef7dc..cfe7938065f9 100644 --- a/sound/soc/codecs/cs35l56-shared-test.c +++ b/sound/soc/codecs/cs35l56-shared-test.c @@ -11,15 +11,23 @@ #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/device/faux.h> +#include <linux/gpio/driver.h> #include <linux/module.h> #include <linux/random.h> #include <linux/regmap.h> #include <linux/seq_buf.h> #include <sound/cs35l56.h> +struct cs35l56_shared_test_mock_gpio { + unsigned int pin_state; + struct gpio_chip chip; +}; + struct cs35l56_shared_test_priv { struct kunit *test; struct faux_device *amp_dev; + struct faux_device *gpio_dev; + struct cs35l56_shared_test_mock_gpio *gpio_priv; struct regmap *registers; struct cs35l56_base *cs35l56_base; u8 applied_pad_pull_state[CS35L56_MAX_GPIO]; @@ -37,6 +45,94 @@ KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy, KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_wrapper, regmap_exit, struct regmap *) +KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper, + device_remove_software_node, + struct device *) + +static int cs35l56_shared_test_mock_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return GPIO_LINE_DIRECTION_IN; +} + +static int cs35l56_shared_test_mock_gpio_direction_in(struct gpio_chip *chip, + unsigned int offset) +{ + return 0; +} + +static int cs35l56_shared_test_mock_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct cs35l56_shared_test_mock_gpio *gpio_priv = gpiochip_get_data(chip); + + return !!(gpio_priv->pin_state & BIT(offset)); +} + +static const struct gpio_chip cs35l56_shared_test_mock_gpio_chip = { + .label = "cs35l56_shared_test_mock_gpio", + .owner = THIS_MODULE, + .get_direction = cs35l56_shared_test_mock_gpio_get_direction, + .direction_input = cs35l56_shared_test_mock_gpio_direction_in, + .get = cs35l56_shared_test_mock_gpio_get, + .base = -1, + .ngpio = 32, +}; + +/* software_node referencing the gpio driver */ +static const struct software_node cs35l56_shared_test_mock_gpio_swnode = { + .name = "cs35l56_shared_test_mock_gpio", +}; + +static int cs35l56_shared_test_mock_gpio_probe(struct faux_device *fdev) +{ + struct cs35l56_shared_test_mock_gpio *gpio_priv; + struct device *dev = &fdev->dev; + int ret; + + gpio_priv = devm_kzalloc(dev, sizeof(*gpio_priv), GFP_KERNEL); + if (!gpio_priv) + return -ENOMEM; + + ret = device_add_software_node(dev, &cs35l56_shared_test_mock_gpio_swnode); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, device_remove_software_node_wrapper, dev); + if (ret) + return ret; + + /* GPIO core modifies our struct gpio_chip so use a copy */ + gpio_priv->chip = cs35l56_shared_test_mock_gpio_chip; + gpio_priv->chip.parent = dev; + ret = devm_gpiochip_add_data(dev, &gpio_priv->chip, gpio_priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to add gpiochip\n"); + + dev_set_drvdata(dev, gpio_priv); + + return 0; +} + +static struct faux_device_ops cs35l56_shared_test_mock_gpio_drv = { + .probe = cs35l56_shared_test_mock_gpio_probe, +}; + +static void _cs35l56_shared_test_create_dummy_gpio(struct kunit *test) +{ + struct cs35l56_shared_test_priv *priv = test->priv; + + priv->gpio_dev = faux_device_create("cs35l56_shared_test_mock_gpio", NULL, + &cs35l56_shared_test_mock_gpio_drv); + KUNIT_ASSERT_NOT_NULL(test, priv->gpio_dev); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, + faux_device_destroy_wrapper, + priv->gpio_dev)); + + priv->gpio_priv = dev_get_drvdata(&priv->gpio_dev->dev); + KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv); +} + static const struct regmap_config cs35l56_shared_test_mock_registers_regmap = { .reg_bits = 32, .val_bits = 32, @@ -410,6 +506,109 @@ static void cs35l56_shared_test_onchip_speaker_id_not_defined(struct kunit *test KUNIT_EXPECT_EQ(test, cs35l56_read_onchip_spkid(cs35l56_base), -ENOENT); } +/* simulate cs_amp_get_vendor_spkid() reading a vendor-specific ID of 1 */ +static int cs35l56_shared_test_get_vendor_spkid_1(struct device *dev) +{ + return 1; +} + +static void cs35l56_shared_test_get_speaker_id_vendor(struct kunit *test) +{ + struct cs35l56_shared_test_priv *priv = test->priv; + + /* Hook cs_amp_get_vendor_spkid() to return an ID of 1 */ + kunit_activate_static_stub(test, cs_amp_get_vendor_spkid, + cs35l56_shared_test_get_vendor_spkid_1); + + KUNIT_EXPECT_EQ(test, cs35l56_get_speaker_id(priv->cs35l56_base), 1); +} + +static void cs35l56_shared_test_get_speaker_id_property(struct kunit *test) +{ + struct cs35l56_shared_test_priv *priv = test->priv; + const struct property_entry dev_props[] = { + PROPERTY_ENTRY_U32("cirrus,speaker-id", 2), + { } + }; + const struct software_node dev_node = SOFTWARE_NODE("SPK1", dev_props, NULL); + + KUNIT_ASSERT_EQ(test, device_add_software_node(priv->cs35l56_base->dev, &dev_node), 0); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, + device_remove_software_node_wrapper, + priv->cs35l56_base->dev)); + + KUNIT_EXPECT_EQ(test, cs35l56_get_speaker_id(priv->cs35l56_base), 2); +} + +/* + * Create software nodes equivalent to ACPI structure + * + * Device(GSPK) { + * Name(_DSD, ...) { + * Package() { + * cs-gpios { + * GPIO, n, 0, + * ... + * } + * } + */ +static void _cs35l56_shared_test_create_spkid_swnode(struct kunit *test, + struct device *dev, + const struct software_node_ref_args *args, + int num_args) +{ + struct cs35l56_shared_test_priv *priv = test->priv; + const struct property_entry props_template[] = { + PROPERTY_ENTRY_REF_ARRAY_LEN("spk-id-gpios", args, num_args), + { } + }; + struct property_entry *props; + struct software_node *node; + + props = kunit_kzalloc(test, sizeof(props_template), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, props); + memcpy(props, props_template, sizeof(props_template)); + + node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, node); + *node = SOFTWARE_NODE("GSPK", props, NULL); + + KUNIT_ASSERT_EQ(test, device_add_software_node(dev, node), 0); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, + device_remove_software_node_wrapper, + priv->cs35l56_base->dev)); +} + +static void cs35l56_shared_test_get_speaker_id_from_host_gpio(struct kunit *test) +{ + const struct cs35l56_shared_test_param *param = test->param_value; + struct cs35l56_shared_test_priv *priv = test->priv; + struct cs35l56_base *cs35l56_base = priv->cs35l56_base; + struct software_node_ref_args *ref; + int i; + + if (!IS_REACHABLE(CONFIG_GPIOLIB)) { + kunit_skip(test, "Requires CONFIG_GPIOLIB"); + return; + } + + _cs35l56_shared_test_create_dummy_gpio(test); + + ref = kunit_kcalloc(test, ARRAY_SIZE(param->spkid_gpios), sizeof(*ref), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ref); + + for (i = 0; param->spkid_gpios[i] >= 0; i++) { + ref[i] = SOFTWARE_NODE_REFERENCE(&cs35l56_shared_test_mock_gpio_swnode, + param->spkid_gpios[i], 0); + } + _cs35l56_shared_test_create_spkid_swnode(test, cs35l56_base->dev, ref, i); + + priv->gpio_priv->pin_state = param->gpio_status; + KUNIT_EXPECT_EQ(test, cs35l56_get_speaker_id(priv->cs35l56_base), param->spkid); +} + static int cs35l56_shared_test_case_regmap_init(struct kunit *test, const struct regmap_config *regmap_config) { @@ -602,6 +801,40 @@ KUNIT_ARRAY_PARAM(cs35l56_shared_test_onchip_spkid_pull, cs35l56_shared_test_onchip_spkid_pull_cases, cs35l56_shared_test_gpio_param_desc); +/* Note: spk-id-gpios property bit order is LSbit...MSbit */ +static const struct cs35l56_shared_test_param cs35l56_shared_test_host_gpio_spkid_cases[] = { + { .spkid_gpios = { 0, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 0, -1 }, .gpio_status = ~BIT(0), .spkid = 0 }, + { .spkid_gpios = { 0, -1 }, .gpio_status = BIT(0), .spkid = 1 }, + + { .spkid_gpios = { 6, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 6, -1 }, .gpio_status = ~BIT(6), .spkid = 0 }, + { .spkid_gpios = { 6, -1 }, .gpio_status = BIT(6), .spkid = 1 }, + + { .spkid_gpios = { 6, 0, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 6, 0, -1 }, .gpio_status = ~(BIT(0) | BIT(6)), .spkid = 0 }, + { .spkid_gpios = { 6, 0, -1 }, .gpio_status = BIT(6), .spkid = 1 }, + { .spkid_gpios = { 6, 0, -1 }, .gpio_status = BIT(0), .spkid = 2 }, + { .spkid_gpios = { 6, 0, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 }, + + { .spkid_gpios = { 0, 6, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 0, 6, -1 }, .gpio_status = ~(BIT(6) | BIT(0)), .spkid = 0 }, + { .spkid_gpios = { 0, 6, -1 }, .gpio_status = BIT(0), .spkid = 1 }, + { .spkid_gpios = { 0, 6, -1 }, .gpio_status = BIT(6), .spkid = 2 }, + { .spkid_gpios = { 0, 6, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 }, + + { .spkid_gpios = { 0, 6, 2, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 0, 6, 2, -1 }, .gpio_status = BIT(0), .spkid = 1 }, + { .spkid_gpios = { 0, 6, 2, -1 }, .gpio_status = BIT(6), .spkid = 2 }, + { .spkid_gpios = { 0, 6, 2, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 }, + { .spkid_gpios = { 0, 6, 2, -1 }, .gpio_status = BIT(2), .spkid = 4 }, + { .spkid_gpios = { 0, 6, 2, -1 }, .gpio_status = BIT(2) | BIT(0), .spkid = 5 }, + { .spkid_gpios = { 0, 6, 2, -1 }, .gpio_status = BIT(2) | BIT(6), .spkid = 6 }, + { .spkid_gpios = { 0, 6, 2, -1 }, .gpio_status = BIT(2) | BIT(6) | BIT(0), .spkid = 7 }, +}; +KUNIT_ARRAY_PARAM(cs35l56_shared_test_host_gpio_spkid, cs35l56_shared_test_host_gpio_spkid_cases, + cs35l56_shared_test_gpio_param_desc); + static struct kunit_case cs35l56_shared_test_cases[] = { /* Tests for speaker id */ KUNIT_CASE_PARAM(cs35l56_shared_test_mock_gpio_status_selftest, @@ -616,6 +849,13 @@ static struct kunit_case cs35l56_shared_test_cases[] = { cs35l56_shared_test_onchip_spkid_pull_gen_params), KUNIT_CASE(cs35l56_shared_test_stash_onchip_spkid_pins_reject_invalid), KUNIT_CASE(cs35l56_shared_test_onchip_speaker_id_not_defined), + + KUNIT_CASE(cs35l56_shared_test_get_speaker_id_vendor), + KUNIT_CASE(cs35l56_shared_test_get_speaker_id_property), + KUNIT_CASE_PARAM_ATTR(cs35l56_shared_test_get_speaker_id_from_host_gpio, + cs35l56_shared_test_host_gpio_spkid_gen_params, + { KUNIT_SPEED_SLOW }), + { } }; diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index af87ebae98cb..e05d975ba794 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -1185,6 +1185,15 @@ ssize_t cs35l56_calibrate_debugfs_write(struct cs35l56_base *cs35l56_base, } EXPORT_SYMBOL_NS_GPL(cs35l56_calibrate_debugfs_write, "SND_SOC_CS35L56_SHARED"); +int cs35l56_factory_calibrate(struct cs35l56_base *cs35l56_base) +{ + if (!IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_PERFORM_CTRL)) + return -ENXIO; + + return cs35l56_perform_calibration(cs35l56_base); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_factory_calibrate, "SND_SOC_CS35L56_SHARED"); + ssize_t cs35l56_cal_ambient_debugfs_write(struct cs35l56_base *cs35l56_base, const char __user *from, size_t count, loff_t *ppos) diff --git a/sound/soc/codecs/cs35l56-test.c b/sound/soc/codecs/cs35l56-test.c index b6c8c08e3ade..124fe5e75500 100644 --- a/sound/soc/codecs/cs35l56-test.c +++ b/sound/soc/codecs/cs35l56-test.c @@ -74,6 +74,78 @@ static const char *cs35l56_test_devm_get_vendor_specific_variant_id_none(struct return ERR_PTR(-ENOENT); } +static void cs35l56_test_system_name_from_ssid(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + cs35l56->speaker_id = -1; + snd_soc_card_set_pci_ssid(cs35l56->component->card, 0x12b4, 0xa7c8); + + KUNIT_EXPECT_EQ(test, cs35l56_get_firmware_uid(cs35l56), 0); + KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0); + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.system_name, "12b4a7c8"); +} + +static void cs35l56_test_system_name_from_ssid_and_spkid(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + cs35l56->speaker_id = 1; + snd_soc_card_set_pci_ssid(cs35l56->component->card, 0x12b4, 0xa7c8); + + KUNIT_EXPECT_EQ(test, cs35l56_get_firmware_uid(cs35l56), 0); + KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0); + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.system_name, "12b4a7c8-spkid1"); +} + +static void cs35l56_test_system_name_from_property(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + const struct property_entry dev_props[] = { + PROPERTY_ENTRY_STRING("cirrus,firmware-uid", "acme"), + { } + }; + const struct software_node dev_node = SOFTWARE_NODE("SPK1", dev_props, NULL); + + cs35l56->speaker_id = -1; + + KUNIT_ASSERT_EQ(test, device_add_software_node(cs35l56->base.dev, &dev_node), 0); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, + device_remove_software_node_wrapper, + cs35l56->base.dev)); + + KUNIT_EXPECT_EQ(test, cs35l56_get_firmware_uid(cs35l56), 0); + KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0); + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.system_name, "acme"); +} + +static void cs35l56_test_system_name_from_property_and_spkid(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + const struct property_entry dev_props[] = { + PROPERTY_ENTRY_STRING("cirrus,firmware-uid", "acme"), + { } + }; + const struct software_node dev_node = SOFTWARE_NODE("SPK1", dev_props, NULL); + + cs35l56->speaker_id = 1; + + KUNIT_ASSERT_EQ(test, device_add_software_node(cs35l56->base.dev, &dev_node), 0); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, + device_remove_software_node_wrapper, + cs35l56->base.dev)); + + KUNIT_EXPECT_EQ(test, cs35l56_get_firmware_uid(cs35l56), 0); + KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0); + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.system_name, "acme-spkid1"); +} + static void cs35l56_test_l56_b0_suffix_sdw(struct kunit *test) { struct cs35l56_test_priv *priv = test->priv; @@ -292,18 +364,17 @@ static void cs35l56_test_parse_xu_onchip_spkid(struct kunit *test) struct cs35l56_test_priv *priv = test->priv; struct cs35l56_private *cs35l56 = priv->cs35l56_priv; struct software_node *ext0_node; - int num_gpios = 0; - int num_pulls = 0; + int num_gpios, num_pulls; int i; - for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++, num_gpios++) { - if (param->spkid_gpios[i] < 0) + for (num_gpios = 0; num_gpios < ARRAY_SIZE(param->spkid_gpios); num_gpios++) { + if (param->spkid_gpios[num_gpios] < 0) break; } KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios)); - for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++, num_pulls++) { - if (param->spkid_pulls[i] < 0) + for (num_pulls = 0; num_pulls < ARRAY_SIZE(param->spkid_pulls); num_pulls++) { + if (param->spkid_pulls[num_pulls] < 0) break; } KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls)); @@ -541,18 +612,28 @@ static void cs35l56_test_gpio_param_desc(const struct cs35l56_test_param *param, } static const struct cs35l56_test_param cs35l56_test_onchip_spkid_cases[] = { + { .spkid_gpios = { 1, -1 }, .spkid_pulls = { -1, -1 }, }, + { .spkid_gpios = { 1, -1 }, .spkid_pulls = { -1, -1 }, }, { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, }, { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, }, + { .spkid_gpios = { 7, -1 }, .spkid_pulls = { -1, -1 }, }, + { .spkid_gpios = { 7, -1 }, .spkid_pulls = { -1, -1 }, }, { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, }, { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, }, + { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { -1, -1, -1 }, }, + { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { -1, -1, -1 }, }, { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, }, { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, }, + { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { -1, -1, -1 }, }, + { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { -1, -1, -1 }, }, { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, }, { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, }, + { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { -1, -1, -1, -1 }, }, + { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { -1, -1, -1, -1 }, }, { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, }, { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, }, }; @@ -586,6 +667,10 @@ KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_all, cs35l56_test_type_rev_all_param_cas cs35l56_test_type_rev_param_desc); static struct kunit_case cs35l56_test_cases_soundwire[] = { + KUNIT_CASE(cs35l56_test_system_name_from_ssid), + KUNIT_CASE(cs35l56_test_system_name_from_ssid_and_spkid), + KUNIT_CASE(cs35l56_test_system_name_from_property), + KUNIT_CASE(cs35l56_test_system_name_from_property_and_spkid), KUNIT_CASE(cs35l56_test_l56_b0_suffix_sdw), KUNIT_CASE_PARAM(cs35l56_test_suffix_sdw, cs35l56_test_type_rev_ex_b0_gen_params), KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_sdw, @@ -603,6 +688,10 @@ static struct kunit_case cs35l56_test_cases_soundwire[] = { }; static struct kunit_case cs35l56_test_cases_not_soundwire[] = { + KUNIT_CASE(cs35l56_test_system_name_from_ssid), + KUNIT_CASE(cs35l56_test_system_name_from_ssid_and_spkid), + KUNIT_CASE(cs35l56_test_system_name_from_property), + KUNIT_CASE(cs35l56_test_system_name_from_property_and_spkid), KUNIT_CASE_PARAM(cs35l56_test_suffix_i2cspi, cs35l56_test_type_rev_all_gen_params), KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_i2cspi, cs35l56_test_type_rev_all_gen_params), diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 37909a319f88..378017fcea10 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1109,6 +1109,88 @@ static int cs35l56_cal_data_ctl_set(struct snd_kcontrol *kcontrol, return 1; } +static int cs35l56_cal_ambient_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = cs35l56->ambient_ctl_value; + + return 0; +} + +static int cs35l56_cal_ambient_ctl_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm; + int temperature = ucontrol->value.integer.value[0]; + int ret; + + if (temperature == cs35l56->ambient_ctl_value) + return 0; + + if ((temperature < 0) || (temperature > 40)) + return -EINVAL; + + dapm = cs35l56_power_up_for_cal(cs35l56); + if (IS_ERR(dapm)) + return PTR_ERR(dapm); + + ret = cs_amp_write_ambient_temp(&cs35l56->dsp.cs_dsp, + cs35l56->base.calibration_controls, + temperature); + cs35l56_power_down_after_cal(cs35l56); + + if (ret) + return ret; + + cs35l56->ambient_ctl_value = temperature; + + return 1; +} + +static int cs35l56_calibrate_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Allow reading because of user-side libraries that assume all + * controls are readable. But always return false to prevent dumb + * save-restore tools like alsactl accidentically triggering a + * factory calibration when they restore. + */ + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int cs35l56_calibrate_ctl_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm; + int ret; + + if (ucontrol->value.integer.value[0] == 0) + return 0; + + dapm = cs35l56_power_up_for_cal(cs35l56); + if (IS_ERR(dapm)) + return PTR_ERR(dapm); + + snd_soc_dapm_mutex_lock(dapm); + ret = cs35l56_factory_calibrate(&cs35l56->base); + snd_soc_dapm_mutex_unlock(dapm); + cs35l56_power_down_after_cal(cs35l56); + if (ret < 0) + return ret; + + return 1; +} + static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = { SND_SOC_BYTES_E("CAL_DATA", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32), cs35l56_cal_data_ctl_get, cs35l56_cal_data_ctl_set), @@ -1117,6 +1199,14 @@ static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = { SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE), }; +static const struct snd_kcontrol_new cs35l56_cal_perform_controls[] = { + SOC_SINGLE_EXT("CAL_AMBIENT", SND_SOC_NOPM, 0, 40, 0, + cs35l56_cal_ambient_ctl_get, cs35l56_cal_ambient_ctl_set), + SOC_SINGLE_BOOL_EXT_ACC("Calibrate Switch", 0, + cs35l56_calibrate_ctl_get, cs35l56_calibrate_ctl_set, + SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_VOLATILE), +}; + VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) { unsigned short vendor, device; @@ -1290,6 +1380,12 @@ static int cs35l56_component_probe(struct snd_soc_component *component) ARRAY_SIZE(cs35l56_cal_data_restore_controls)); } + if (!ret && IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_PERFORM_CTRL)) { + ret = snd_soc_add_component_controls(component, + cs35l56_cal_perform_controls, + ARRAY_SIZE(cs35l56_cal_perform_controls)); + } + if (ret) return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n"); @@ -1669,7 +1765,7 @@ VISIBLE_IF_KUNIT int cs35l56_process_xu_properties(struct cs35l56_private *cs35l } EXPORT_SYMBOL_IF_KUNIT(cs35l56_process_xu_properties); -static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56) +VISIBLE_IF_KUNIT int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56) { struct device *dev = cs35l56->base.dev; const char *prop; @@ -1694,6 +1790,7 @@ static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56) return 0; } +EXPORT_SYMBOL_IF_KUNIT(cs35l56_get_firmware_uid); /* * Some SoundWire laptops have a spk-id-gpios property but it points to diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 691f857d0bd8..cd71b23b2a3a 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -42,6 +42,7 @@ struct cs35l56_private { bool sdw_irq_no_unmask; bool soft_resetting; bool sdw_attached; + bool sdw_in_clock_stop_1; struct completion init_completion; int speaker_id; @@ -53,6 +54,8 @@ struct cs35l56_private { bool sysclk_set; u8 sdw_link_num; u8 sdw_unique_id; + + u8 ambient_ctl_value; }; static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_base *cs35l56_base) @@ -78,6 +81,7 @@ void cs35l56_remove(struct cs35l56_private *cs35l56); int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56); int cs35l56_set_fw_name(struct snd_soc_component *component); int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56); +int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56); #endif #endif /* ifndef CS35L56_H */ diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 3139f03cd42b..a48980e746ff 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -1,12 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * CS4270 ALSA SoC (ASoC) codec driver * * Author: Timur Tabi <timur@freescale.com> * - * Copyright 2007-2009 Freescale Semiconductor, Inc. This file is licensed - * under the terms of the GNU General Public License version 2. This - * program is licensed "as is" without any warranty of any kind, whether - * express or implied. + * Copyright 2007-2009 Freescale Semiconductor, Inc. * * This is an ASoC device driver for the Cirrus Logic CS4270 codec. * diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index fd02d8a57e0f..f0d6ff0b2976 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -45,12 +45,25 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs42l43_##name##_enum, reg, \ static const struct snd_kcontrol_new cs42l43_##name##_mux = \ SOC_DAPM_ENUM("Route", cs42l43_##name##_enum) +#define CS42L43B_DECL_MUX(name, reg) \ +static SOC_VALUE_ENUM_SINGLE_DECL(cs42l43_##name##_enum, reg, \ + 0, CS42L43_MIXER_SRC_MASK, \ + cs42l43b_mixer_texts, cs42l43b_mixer_values); \ +static const struct snd_kcontrol_new cs42l43_##name##_mux = \ + SOC_DAPM_ENUM("Route", cs42l43_##name##_enum) + #define CS42L43_DECL_MIXER(name, reg) \ CS42L43_DECL_MUX(name##_in1, reg); \ CS42L43_DECL_MUX(name##_in2, reg + 0x4); \ CS42L43_DECL_MUX(name##_in3, reg + 0x8); \ CS42L43_DECL_MUX(name##_in4, reg + 0xC) +#define CS42L43B_DECL_MIXER(name, reg) \ + CS42L43B_DECL_MUX(name##_in1, reg); \ + CS42L43B_DECL_MUX(name##_in2, reg + 0x4); \ + CS42L43B_DECL_MUX(name##_in3, reg + 0x8); \ + CS42L43B_DECL_MUX(name##_in4, reg + 0xC) + #define CS42L43_DAPM_MUX(name_str, name) \ SND_SOC_DAPM_MUX(name_str " Input", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_mux) @@ -99,11 +112,23 @@ static const struct snd_kcontrol_new cs42l43_##name##_mux = \ { name_str, "EQ1", "EQ" }, \ { name_str, "EQ2", "EQ" } +#define CS42L43B_BASE_ROUTES(name_str) \ + { name_str, "Decimator 5", "Decimator 5" }, \ + { name_str, "Decimator 6", "Decimator 6" }, \ + { name_str, "ISRC1 DEC3", "ISRC1DEC3" }, \ + { name_str, "ISRC1 DEC4", "ISRC1DEC4" }, \ + { name_str, "ISRC2 DEC3", "ISRC2DEC3" }, \ + { name_str, "ISRC2 DEC4", "ISRC2DEC4" } + #define CS42L43_MUX_ROUTES(name_str, widget) \ { widget, NULL, name_str " Input" }, \ { name_str " Input", NULL, "Mixer Core" }, \ CS42L43_BASE_ROUTES(name_str " Input") +#define CS42L43B_MUX_ROUTES(name_str, widget) \ + CS42L43_MUX_ROUTES(name_str, widget), \ + CS42L43B_BASE_ROUTES(name_str " Input") + #define CS42L43_MIXER_ROUTES(name_str, widget) \ { name_str " Mixer", NULL, name_str " Input 1" }, \ { name_str " Mixer", NULL, name_str " Input 2" }, \ @@ -116,6 +141,13 @@ static const struct snd_kcontrol_new cs42l43_##name##_mux = \ CS42L43_BASE_ROUTES(name_str " Input 3"), \ CS42L43_BASE_ROUTES(name_str " Input 4") +#define CS42L43B_MIXER_ROUTES(name_str, widget) \ + CS42L43_MIXER_ROUTES(name_str, widget), \ + CS42L43B_BASE_ROUTES(name_str " Input 1"), \ + CS42L43B_BASE_ROUTES(name_str " Input 2"), \ + CS42L43B_BASE_ROUTES(name_str " Input 3"), \ + CS42L43B_BASE_ROUTES(name_str " Input 4") + #define CS42L43_MIXER_VOLUMES(name_str, base) \ SOC_SINGLE_RANGE_TLV(name_str " Input 1 Volume", base, \ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ @@ -300,6 +332,7 @@ static int cs42l43_startup(struct snd_pcm_substream *substream, struct snd_soc_d struct snd_soc_component *component = dai->component; struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); struct cs42l43 *cs42l43 = priv->core; + int ret; int provider = !dai->id || !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, CS42L43_ASP_MASTER_MODE_MASK); @@ -309,6 +342,14 @@ static int cs42l43_startup(struct snd_pcm_substream *substream, struct snd_soc_d else priv->constraint.mask = CS42L43_CONSUMER_RATE_MASK; + if (cs42l43->variant_id == CS42L43_DEVID_VAL && (dai->id == 3 || dai->id == 4)) { + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 1, 2); + if (ret < 0) + return ret; + } + return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &priv->constraint); @@ -590,12 +631,25 @@ static int cs42l43_dai_probe(struct snd_soc_dai *dai) "Decimator 2 Switch", "Decimator 3 Switch", "Decimator 4 Switch", + "Decimator 5 Switch", + "Decimator 6 Switch", }; - int i; + int control_size, i; static_assert(ARRAY_SIZE(controls) == ARRAY_SIZE(priv->kctl)); - for (i = 0; i < ARRAY_SIZE(controls); i++) { + switch (priv->core->variant_id) { + case CS42L43_DEVID_VAL: + control_size = ARRAY_SIZE(controls) - 2; // ignore Decimator 5 and 6 + break; + case CS42L43B_DEVID_VAL: + control_size = ARRAY_SIZE(controls); + break; + default: + return -EINVAL; + } + + for (i = 0; i < control_size; i++) { if (priv->kctl[i]) continue; @@ -703,7 +757,7 @@ static struct snd_soc_dai_driver cs42l43_dais[] = { .capture = { .stream_name = "DP3 Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 4, .rates = SNDRV_PCM_RATE_KNOT, .formats = CS42L43_SDW_FORMATS, }, @@ -715,7 +769,7 @@ static struct snd_soc_dai_driver cs42l43_dais[] = { .capture = { .stream_name = "DP4 Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 4, .rates = SNDRV_PCM_RATE_KNOT, .formats = CS42L43_SDW_FORMATS, }, @@ -808,6 +862,10 @@ static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL3 CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL4, CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec5_wnf_corner, CS42L43B_DECIM_HPF_WNF_CTRL5, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec6_wnf_corner, CS42L43B_DECIM_HPF_WNF_CTRL6, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); static const char * const cs42l43_hpf_corner_text[] = { "3Hz", "12Hz", "48Hz", "96Hz", @@ -821,6 +879,10 @@ static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL3 CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL4, CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec5_hpf_corner, CS42L43B_DECIM_HPF_WNF_CTRL5, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec6_hpf_corner, CS42L43B_DECIM_HPF_WNF_CTRL6, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2, CS42L43_DECIM1_VI_RAMP_SHIFT, cs42l43_ramp_text); @@ -839,6 +901,31 @@ static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4 static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4, CS42L43_DECIM4_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec1_ramp_up, CS42L43B_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec1_ramp_down, CS42L43B_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec2_ramp_up, CS42L43B_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec2_ramp_down, CS42L43B_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec3_ramp_up, CS42L43B_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec3_ramp_down, CS42L43B_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec4_ramp_up, CS42L43B_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec4_ramp_down, CS42L43B_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec5_ramp_up, CS42L43B_DECIM_VOL_CTRL_CH5_CH6, + CS42L43B_DECIM5_PATH1_VOL_RISE_RATE_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec5_ramp_down, CS42L43B_DECIM_VOL_CTRL_CH5_CH6, + CS42L43B_DECIM5_PATH1_VOL_FALL_RATE_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec6_ramp_up, CS42L43B_DECIM_VOL_CTRL_CH5_CH6, + CS42L43B_DECIM6_PATH1_VOL_RISE_RATE_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43b_dec6_ramp_down, CS42L43B_DECIM_VOL_CTRL_CH5_CH6, + CS42L43B_DECIM6_PATH1_VOL_FALL_RATE_SHIFT, cs42l43_ramp_text); + static DECLARE_TLV_DB_SCALE(cs42l43_speaker_tlv, -6400, 50, 0); static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_up, CS42L43_AMP1_2_VOL_RAMP, @@ -898,6 +985,37 @@ static const unsigned int cs42l43_mixer_values[] = { 0x58, 0x59, // EQ1, 2 }; +static const char * const cs42l43b_mixer_texts[] = { + "None", + "Tone Generator 1", "Tone Generator 2", + "Decimator 1", "Decimator 2", "Decimator 3", "Decimator 4", "Decimator 5", "Decimator 6", + "ASPRX1", "ASPRX2", "ASPRX3", "ASPRX4", "ASPRX5", "ASPRX6", + "DP5RX1", "DP5RX2", "DP6RX1", "DP6RX2", "DP7RX1", "DP7RX2", + "ASRC INT1", "ASRC INT2", "ASRC INT3", "ASRC INT4", + "ASRC DEC1", "ASRC DEC2", "ASRC DEC3", "ASRC DEC4", + "ISRC1 INT1", "ISRC1 INT2", + "ISRC1 DEC1", "ISRC1 DEC2", "ISRC1 DEC3", "ISRC1 DEC4", + "ISRC2 INT1", "ISRC2 INT2", + "ISRC2 DEC1", "ISRC2 DEC2", "ISRC2 DEC3", "ISRC2 DEC4", + "EQ1", "EQ2", +}; + +static const unsigned int cs42l43b_mixer_values[] = { + 0x00, // None + 0x04, 0x05, // Tone Generator 1, 2 + 0x10, 0x11, 0x80, 0x81, 0x12, 0x13, // Decimator 1, 2, 3, 4, 5, 6 + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, // ASPRX1,2,3,4,5,6 + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, // DP5, 6, 7RX1, 2 + 0x40, 0x41, 0x42, 0x43, // ASRC INT1, 2, 3, 4 + 0x44, 0x45, 0x46, 0x47, // ASRC DEC1, 2, 3, 4 + 0x50, 0x51, // ISRC1 INT1, 2 + 0x52, 0x53, 0x78, 0x79, // ISRC1 DEC1, 2, 3, 4 + 0x54, 0x55, // ISRC2 INT1, 2 + 0x56, 0x57, 0x7A, 0x7B, // ISRC2 DEC1, 2, 3, 4 + 0x58, 0x59, // EQ1, 2 +}; + +/* A variant */ CS42L43_DECL_MUX(asptx1, CS42L43_ASPTX1_INPUT); CS42L43_DECL_MUX(asptx2, CS42L43_ASPTX2_INPUT); CS42L43_DECL_MUX(asptx3, CS42L43_ASPTX3_INPUT); @@ -946,6 +1064,63 @@ CS42L43_DECL_MIXER(amp2, CS42L43_AMP2MIX_INPUT1); CS42L43_DECL_MIXER(amp3, CS42L43_AMP3MIX_INPUT1); CS42L43_DECL_MIXER(amp4, CS42L43_AMP4MIX_INPUT1); +/* B variant */ +CS42L43B_DECL_MUX(b_asptx1, CS42L43_ASPTX1_INPUT); +CS42L43B_DECL_MUX(b_asptx2, CS42L43_ASPTX2_INPUT); +CS42L43B_DECL_MUX(b_asptx3, CS42L43_ASPTX3_INPUT); +CS42L43B_DECL_MUX(b_asptx4, CS42L43_ASPTX4_INPUT); +CS42L43B_DECL_MUX(b_asptx5, CS42L43_ASPTX5_INPUT); +CS42L43B_DECL_MUX(b_asptx6, CS42L43_ASPTX6_INPUT); + +CS42L43B_DECL_MUX(b_dp1tx1, CS42L43_SWIRE_DP1_CH1_INPUT); +CS42L43B_DECL_MUX(b_dp1tx2, CS42L43_SWIRE_DP1_CH2_INPUT); +CS42L43B_DECL_MUX(b_dp1tx3, CS42L43_SWIRE_DP1_CH3_INPUT); +CS42L43B_DECL_MUX(b_dp1tx4, CS42L43_SWIRE_DP1_CH4_INPUT); +CS42L43B_DECL_MUX(b_dp2tx1, CS42L43_SWIRE_DP2_CH1_INPUT); +CS42L43B_DECL_MUX(b_dp2tx2, CS42L43_SWIRE_DP2_CH2_INPUT); +CS42L43B_DECL_MUX(b_dp3tx1, CS42L43_SWIRE_DP3_CH1_INPUT); +CS42L43B_DECL_MUX(b_dp3tx2, CS42L43_SWIRE_DP3_CH2_INPUT); +CS42L43B_DECL_MUX(b_dp3tx3, CS42L43B_SWIRE_DP3_CH3_INPUT); +CS42L43B_DECL_MUX(b_dp3tx4, CS42L43B_SWIRE_DP3_CH4_INPUT); +CS42L43B_DECL_MUX(b_dp4tx1, CS42L43_SWIRE_DP4_CH1_INPUT); +CS42L43B_DECL_MUX(b_dp4tx2, CS42L43_SWIRE_DP4_CH2_INPUT); +CS42L43B_DECL_MUX(b_dp4tx3, CS42L43B_SWIRE_DP4_CH3_INPUT); +CS42L43B_DECL_MUX(b_dp4tx4, CS42L43B_SWIRE_DP4_CH4_INPUT); + +CS42L43B_DECL_MUX(b_asrcint1, CS42L43_ASRC_INT1_INPUT1); +CS42L43B_DECL_MUX(b_asrcint2, CS42L43_ASRC_INT2_INPUT1); +CS42L43B_DECL_MUX(b_asrcint3, CS42L43_ASRC_INT3_INPUT1); +CS42L43B_DECL_MUX(b_asrcint4, CS42L43_ASRC_INT4_INPUT1); +CS42L43B_DECL_MUX(b_asrcdec1, CS42L43_ASRC_DEC1_INPUT1); +CS42L43B_DECL_MUX(b_asrcdec2, CS42L43_ASRC_DEC2_INPUT1); +CS42L43B_DECL_MUX(b_asrcdec3, CS42L43_ASRC_DEC3_INPUT1); +CS42L43B_DECL_MUX(b_asrcdec4, CS42L43_ASRC_DEC4_INPUT1); + +CS42L43B_DECL_MUX(b_isrc1int1, CS42L43_ISRC1INT1_INPUT1); +CS42L43B_DECL_MUX(b_isrc1int2, CS42L43_ISRC1INT2_INPUT1); +CS42L43B_DECL_MUX(b_isrc1dec1, CS42L43_ISRC1DEC1_INPUT1); +CS42L43B_DECL_MUX(b_isrc1dec2, CS42L43_ISRC1DEC2_INPUT1); +CS42L43B_DECL_MUX(b_isrc1dec3, CS42L43B_ISRC1DEC3_INPUT1); +CS42L43B_DECL_MUX(b_isrc1dec4, CS42L43B_ISRC1DEC4_INPUT1); +CS42L43B_DECL_MUX(b_isrc2int1, CS42L43_ISRC2INT1_INPUT1); +CS42L43B_DECL_MUX(b_isrc2int2, CS42L43_ISRC2INT2_INPUT1); +CS42L43B_DECL_MUX(b_isrc2dec1, CS42L43_ISRC2DEC1_INPUT1); +CS42L43B_DECL_MUX(b_isrc2dec2, CS42L43_ISRC2DEC2_INPUT1); +CS42L43B_DECL_MUX(b_isrc2dec3, CS42L43B_ISRC2DEC3_INPUT1); +CS42L43B_DECL_MUX(b_isrc2dec4, CS42L43B_ISRC2DEC4_INPUT1); + +CS42L43B_DECL_MUX(b_spdif1, CS42L43_SPDIF1_INPUT1); +CS42L43B_DECL_MUX(b_spdif2, CS42L43_SPDIF2_INPUT1); + +CS42L43B_DECL_MIXER(b_eq1, CS42L43_EQ1MIX_INPUT1); +CS42L43B_DECL_MIXER(b_eq2, CS42L43_EQ2MIX_INPUT1); + +CS42L43B_DECL_MIXER(b_amp1, CS42L43_AMP1MIX_INPUT1); +CS42L43B_DECL_MIXER(b_amp2, CS42L43_AMP2MIX_INPUT1); + +CS42L43B_DECL_MIXER(b_amp3, CS42L43_AMP3MIX_INPUT1); +CS42L43B_DECL_MIXER(b_amp4, CS42L43_AMP4MIX_INPUT1); + static int cs42l43_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1174,44 +1349,6 @@ static const struct snd_kcontrol_new cs42l43_controls[] = { SOC_ENUM("Decimator 3 HPF Corner Frequency", cs42l43_dec3_hpf_corner), SOC_ENUM("Decimator 4 HPF Corner Frequency", cs42l43_dec4_hpf_corner), - SOC_SINGLE_TLV("Decimator 1 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, - CS42L43_DECIM1_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), - SOC_SINGLE_EXT("Decimator 1 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, - CS42L43_DECIM1_MUTE_SHIFT, 1, 1, - cs42l43_decim_get, cs42l43_dapm_put_volsw), - SOC_SINGLE_TLV("Decimator 2 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, - CS42L43_DECIM2_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), - SOC_SINGLE_EXT("Decimator 2 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, - CS42L43_DECIM2_MUTE_SHIFT, 1, 1, - cs42l43_decim_get, cs42l43_dapm_put_volsw), - SOC_SINGLE_TLV("Decimator 3 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, - CS42L43_DECIM3_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), - SOC_SINGLE_EXT("Decimator 3 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, - CS42L43_DECIM3_MUTE_SHIFT, 1, 1, - cs42l43_decim_get, cs42l43_dapm_put_volsw), - SOC_SINGLE_TLV("Decimator 4 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, - CS42L43_DECIM4_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), - SOC_SINGLE_EXT("Decimator 4 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, - CS42L43_DECIM4_MUTE_SHIFT, 1, 1, - cs42l43_decim_get, cs42l43_dapm_put_volsw), - - SOC_ENUM_EXT("Decimator 1 Ramp Up", cs42l43_dec1_ramp_up, - cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), - SOC_ENUM_EXT("Decimator 1 Ramp Down", cs42l43_dec1_ramp_down, - cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), - SOC_ENUM_EXT("Decimator 2 Ramp Up", cs42l43_dec2_ramp_up, - cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), - SOC_ENUM_EXT("Decimator 2 Ramp Down", cs42l43_dec2_ramp_down, - cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), - SOC_ENUM_EXT("Decimator 3 Ramp Up", cs42l43_dec3_ramp_up, - cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), - SOC_ENUM_EXT("Decimator 3 Ramp Down", cs42l43_dec3_ramp_down, - cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), - SOC_ENUM_EXT("Decimator 4 Ramp Up", cs42l43_dec4_ramp_up, - cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), - SOC_ENUM_EXT("Decimator 4 Ramp Down", cs42l43_dec4_ramp_down, - cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), - SOC_DOUBLE_R_EXT("Speaker Digital Switch", CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2, CS42L43_AMP_MUTE_SHIFT, 1, 1, @@ -1601,35 +1738,81 @@ static int cs42l43_mic_ev(struct snd_soc_dapm_widget *w, unsigned int *val; int ret; - switch (w->shift) { - case CS42L43_ADC1_EN_SHIFT: - case CS42L43_PDM1_DIN_L_EN_SHIFT: - reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; - ramp = CS42L43_DECIM1_VD_RAMP_MASK; - mute = CS42L43_DECIM1_MUTE_MASK; - val = &priv->decim_cache[0]; - break; - case CS42L43_ADC2_EN_SHIFT: - case CS42L43_PDM1_DIN_R_EN_SHIFT: - reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; - ramp = CS42L43_DECIM2_VD_RAMP_MASK; - mute = CS42L43_DECIM2_MUTE_MASK; - val = &priv->decim_cache[1]; - break; - case CS42L43_PDM2_DIN_L_EN_SHIFT: - reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; - ramp = CS42L43_DECIM3_VD_RAMP_MASK; - mute = CS42L43_DECIM3_MUTE_MASK; - val = &priv->decim_cache[2]; - break; - case CS42L43_PDM2_DIN_R_EN_SHIFT: - reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; - ramp = CS42L43_DECIM4_VD_RAMP_MASK; - mute = CS42L43_DECIM4_MUTE_MASK; - val = &priv->decim_cache[3]; - break; - default: - dev_err(priv->dev, "Invalid microphone shift: %d\n", w->shift); + if (cs42l43->variant_id == CS42L43_DEVID_VAL) { + switch (w->shift) { + case CS42L43_ADC1_EN_SHIFT: + case CS42L43_PDM1_DIN_L_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM1_VD_RAMP_MASK; + mute = CS42L43_DECIM1_MUTE_MASK; + val = &priv->decim_cache[0]; + break; + case CS42L43_ADC2_EN_SHIFT: + case CS42L43_PDM1_DIN_R_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM2_VD_RAMP_MASK; + mute = CS42L43_DECIM2_MUTE_MASK; + val = &priv->decim_cache[1]; + break; + case CS42L43_PDM2_DIN_L_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM3_VD_RAMP_MASK; + mute = CS42L43_DECIM3_MUTE_MASK; + val = &priv->decim_cache[2]; + break; + case CS42L43_PDM2_DIN_R_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM4_VD_RAMP_MASK; + mute = CS42L43_DECIM4_MUTE_MASK; + val = &priv->decim_cache[3]; + break; + default: + dev_err(priv->dev, "Invalid microphone shift: %d\n", w->shift); + return -EINVAL; + } + } else if (cs42l43->variant_id == CS42L43B_DEVID_VAL) { + switch (w->shift) { + case CS42L43_ADC1_EN_SHIFT: + reg = CS42L43B_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM1_VD_RAMP_MASK; + mute = CS42L43_DECIM1_MUTE_MASK; + val = &priv->decim_cache[0]; + break; + case CS42L43_ADC2_EN_SHIFT: + reg = CS42L43B_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM2_VD_RAMP_MASK; + mute = CS42L43_DECIM2_MUTE_MASK; + val = &priv->decim_cache[1]; + break; + case CS42L43_PDM1_DIN_L_EN_SHIFT: + reg = CS42L43B_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM3_VD_RAMP_MASK; + mute = CS42L43_DECIM3_MUTE_MASK; + val = &priv->decim_cache[2]; + break; + case CS42L43_PDM1_DIN_R_EN_SHIFT: + reg = CS42L43B_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM4_VD_RAMP_MASK; + mute = CS42L43_DECIM4_MUTE_MASK; + val = &priv->decim_cache[3]; + break; + case CS42L43_PDM2_DIN_L_EN_SHIFT: + reg = CS42L43B_DECIM_VOL_CTRL_CH5_CH6; + ramp = CS42L43B_DECIM5_PATH1_VOL_FALL_RATE_MASK; + mute = CS42L43B_DECIM5_MUTE_MASK; + val = &priv->decim_cache[4]; + break; + case CS42L43_PDM2_DIN_R_EN_SHIFT: + reg = CS42L43B_DECIM_VOL_CTRL_CH5_CH6; + ramp = CS42L43B_DECIM6_PATH1_VOL_FALL_RATE_MASK; + mute = CS42L43B_DECIM6_MUTE_MASK; + val = &priv->decim_cache[5]; + break; + default: + dev_err(priv->dev, "Invalid microphone shift: %d\n", w->shift); + return -EINVAL; + } + } else { return -EINVAL; } @@ -1722,11 +1905,6 @@ static const struct snd_soc_dapm_widget cs42l43_widgets[] = { 0, NULL, 0, cs42l43_mic_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_MUX("Decimator 1 Mode", SND_SOC_NOPM, 0, 0, - &cs42l43_dec_mode_ctl[0]), - SND_SOC_DAPM_MUX("Decimator 2 Mode", SND_SOC_NOPM, 0, 0, - &cs42l43_dec_mode_ctl[1]), - SND_SOC_DAPM_PGA("Decimator 1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("Decimator 2", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("Decimator 3", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1871,53 +2049,6 @@ static const struct snd_soc_dapm_widget cs42l43_widgets[] = { SND_SOC_DAPM_SUPPLY("Mixer Core", CS42L43_BLOCK_EN6, CS42L43_MIXER_EN_SHIFT, 0, NULL, 0), - CS42L43_DAPM_MUX("ASPTX1", asptx1), - CS42L43_DAPM_MUX("ASPTX2", asptx2), - CS42L43_DAPM_MUX("ASPTX3", asptx3), - CS42L43_DAPM_MUX("ASPTX4", asptx4), - CS42L43_DAPM_MUX("ASPTX5", asptx5), - CS42L43_DAPM_MUX("ASPTX6", asptx6), - - CS42L43_DAPM_MUX("DP1TX1", dp1tx1), - CS42L43_DAPM_MUX("DP1TX2", dp1tx2), - CS42L43_DAPM_MUX("DP1TX3", dp1tx3), - CS42L43_DAPM_MUX("DP1TX4", dp1tx4), - CS42L43_DAPM_MUX("DP2TX1", dp2tx1), - CS42L43_DAPM_MUX("DP2TX2", dp2tx2), - CS42L43_DAPM_MUX("DP3TX1", dp3tx1), - CS42L43_DAPM_MUX("DP3TX2", dp3tx2), - CS42L43_DAPM_MUX("DP4TX1", dp4tx1), - CS42L43_DAPM_MUX("DP4TX2", dp4tx2), - - CS42L43_DAPM_MUX("ASRC INT1", asrcint1), - CS42L43_DAPM_MUX("ASRC INT2", asrcint2), - CS42L43_DAPM_MUX("ASRC INT3", asrcint3), - CS42L43_DAPM_MUX("ASRC INT4", asrcint4), - CS42L43_DAPM_MUX("ASRC DEC1", asrcdec1), - CS42L43_DAPM_MUX("ASRC DEC2", asrcdec2), - CS42L43_DAPM_MUX("ASRC DEC3", asrcdec3), - CS42L43_DAPM_MUX("ASRC DEC4", asrcdec4), - - CS42L43_DAPM_MUX("ISRC1INT1", isrc1int1), - CS42L43_DAPM_MUX("ISRC1INT2", isrc1int2), - CS42L43_DAPM_MUX("ISRC1DEC1", isrc1dec1), - CS42L43_DAPM_MUX("ISRC1DEC2", isrc1dec2), - CS42L43_DAPM_MUX("ISRC2INT1", isrc2int1), - CS42L43_DAPM_MUX("ISRC2INT2", isrc2int2), - CS42L43_DAPM_MUX("ISRC2DEC1", isrc2dec1), - CS42L43_DAPM_MUX("ISRC2DEC2", isrc2dec2), - - CS42L43_DAPM_MUX("SPDIF1", spdif1), - CS42L43_DAPM_MUX("SPDIF2", spdif2), - - CS42L43_DAPM_MIXER("EQ1", eq1), - CS42L43_DAPM_MIXER("EQ2", eq2), - - CS42L43_DAPM_MIXER("Speaker L", amp1), - CS42L43_DAPM_MIXER("Speaker R", amp2), - - CS42L43_DAPM_MIXER("Headphone L", amp3), - CS42L43_DAPM_MIXER("Headphone R", amp4), }; static const struct snd_soc_dapm_route cs42l43_routes[] = { @@ -1963,16 +2094,6 @@ static const struct snd_soc_dapm_route cs42l43_routes[] = { { "PDM2L", NULL, "PDM2_DIN" }, { "PDM2R", NULL, "PDM2_DIN" }, - { "Decimator 1 Mode", "PDM", "PDM1L" }, - { "Decimator 1 Mode", "ADC", "ADC1" }, - { "Decimator 2 Mode", "PDM", "PDM1R" }, - { "Decimator 2 Mode", "ADC", "ADC2" }, - - { "Decimator 1", NULL, "Decimator 1 Mode" }, - { "Decimator 2", NULL, "Decimator 2 Mode" }, - { "Decimator 3", NULL, "PDM2L" }, - { "Decimator 4", NULL, "PDM2R" }, - { "ASP Capture", NULL, "ASPTX1" }, { "ASP Capture", NULL, "ASPTX2" }, { "ASP Capture", NULL, "ASPTX3" }, @@ -2060,6 +2181,261 @@ static const struct snd_soc_dapm_route cs42l43_routes[] = { { "ASRC_DEC4", NULL, "ASRC_DEC" }, { "EQ", NULL, "EQ Clock" }, +}; + +static const struct snd_kcontrol_new cs42l43_a_controls[] = { + SOC_ENUM_EXT("Decimator 1 Ramp Up", cs42l43_dec1_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 1 Ramp Down", cs42l43_dec1_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Up", cs42l43_dec2_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Down", cs42l43_dec2_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Up", cs42l43_dec3_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Down", cs42l43_dec3_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Up", cs42l43_dec4_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Down", cs42l43_dec4_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + + SOC_SINGLE_TLV("Decimator 1 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 1 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 2 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 2 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 3 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 3 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 4 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 4 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), +}; + +static const struct snd_kcontrol_new cs42l43_b_controls[] = { + SOC_SINGLE_TLV("Decimator 1 Volume", CS42L43B_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 1 Switch", CS42L43B_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 2 Volume", CS42L43B_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 2 Switch", CS42L43B_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 3 Volume", CS42L43B_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 3 Switch", CS42L43B_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 4 Volume", CS42L43B_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 4 Switch", CS42L43B_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 5 Volume", CS42L43B_DECIM_VOL_CTRL_CH5_CH6, + CS42L43B_DECIM5_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 5 Switch", CS42L43B_DECIM_VOL_CTRL_CH5_CH6, + CS42L43B_DECIM5_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 6 Volume", CS42L43B_DECIM_VOL_CTRL_CH5_CH6, + CS42L43B_DECIM6_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 6 Switch", CS42L43B_DECIM_VOL_CTRL_CH5_CH6, + CS42L43B_DECIM6_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + + SOC_ENUM_EXT("Decimator 1 Ramp Up", cs42l43b_dec1_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 1 Ramp Down", cs42l43b_dec1_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Up", cs42l43b_dec2_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Down", cs42l43b_dec2_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Up", cs42l43b_dec3_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Down", cs42l43b_dec3_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Up", cs42l43b_dec4_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Down", cs42l43b_dec4_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 5 Ramp Up", cs42l43b_dec5_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 5 Ramp Down", cs42l43b_dec5_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 6 Ramp Up", cs42l43b_dec6_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 6 Ramp Down", cs42l43b_dec6_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + + SOC_SINGLE("Decimator 5 WNF Switch", CS42L43B_DECIM_HPF_WNF_CTRL5, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 6 WNF Switch", CS42L43B_DECIM_HPF_WNF_CTRL6, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + + SOC_ENUM("Decimator 5 WNF Corner Frequency", cs42l43b_dec5_wnf_corner), + SOC_ENUM("Decimator 6 WNF Corner Frequency", cs42l43b_dec6_wnf_corner), + + SOC_SINGLE("Decimator 5 HPF Switch", CS42L43B_DECIM_HPF_WNF_CTRL5, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 6 HPF Switch", CS42L43B_DECIM_HPF_WNF_CTRL6, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + + SOC_ENUM("Decimator 5 HPF Corner Frequency", cs42l43b_dec5_hpf_corner), + SOC_ENUM("Decimator 6 HPF Corner Frequency", cs42l43b_dec6_hpf_corner), +}; + +static const struct snd_soc_dapm_widget cs42l43_a_widgets[] = { + SND_SOC_DAPM_MUX("Decimator 1 Mode", SND_SOC_NOPM, 0, 0, + &cs42l43_dec_mode_ctl[0]), + SND_SOC_DAPM_MUX("Decimator 2 Mode", SND_SOC_NOPM, 0, 0, + &cs42l43_dec_mode_ctl[1]), + CS42L43_DAPM_MUX("ASPTX1", asptx1), + CS42L43_DAPM_MUX("ASPTX2", asptx2), + CS42L43_DAPM_MUX("ASPTX3", asptx3), + CS42L43_DAPM_MUX("ASPTX4", asptx4), + CS42L43_DAPM_MUX("ASPTX5", asptx5), + CS42L43_DAPM_MUX("ASPTX6", asptx6), + + CS42L43_DAPM_MUX("DP1TX1", dp1tx1), + CS42L43_DAPM_MUX("DP1TX2", dp1tx2), + CS42L43_DAPM_MUX("DP1TX3", dp1tx3), + CS42L43_DAPM_MUX("DP1TX4", dp1tx4), + CS42L43_DAPM_MUX("DP2TX1", dp2tx1), + CS42L43_DAPM_MUX("DP2TX2", dp2tx2), + CS42L43_DAPM_MUX("DP3TX1", dp3tx1), + CS42L43_DAPM_MUX("DP3TX2", dp3tx2), + CS42L43_DAPM_MUX("DP4TX1", dp4tx1), + CS42L43_DAPM_MUX("DP4TX2", dp4tx2), + + CS42L43_DAPM_MUX("ASRC INT1", asrcint1), + CS42L43_DAPM_MUX("ASRC INT2", asrcint2), + CS42L43_DAPM_MUX("ASRC INT3", asrcint3), + CS42L43_DAPM_MUX("ASRC INT4", asrcint4), + CS42L43_DAPM_MUX("ASRC DEC1", asrcdec1), + CS42L43_DAPM_MUX("ASRC DEC2", asrcdec2), + CS42L43_DAPM_MUX("ASRC DEC3", asrcdec3), + CS42L43_DAPM_MUX("ASRC DEC4", asrcdec4), + + CS42L43_DAPM_MUX("ISRC1INT1", isrc1int1), + CS42L43_DAPM_MUX("ISRC1INT2", isrc1int2), + CS42L43_DAPM_MUX("ISRC1DEC1", isrc1dec1), + CS42L43_DAPM_MUX("ISRC1DEC2", isrc1dec2), + CS42L43_DAPM_MUX("ISRC2INT1", isrc2int1), + CS42L43_DAPM_MUX("ISRC2INT2", isrc2int2), + CS42L43_DAPM_MUX("ISRC2DEC1", isrc2dec1), + CS42L43_DAPM_MUX("ISRC2DEC2", isrc2dec2), + + CS42L43_DAPM_MUX("SPDIF1", spdif1), + CS42L43_DAPM_MUX("SPDIF2", spdif2), + + CS42L43_DAPM_MIXER("EQ1", eq1), + CS42L43_DAPM_MIXER("EQ2", eq2), + + CS42L43_DAPM_MIXER("Speaker L", amp1), + CS42L43_DAPM_MIXER("Speaker R", amp2), + + CS42L43_DAPM_MIXER("Headphone L", amp3), + CS42L43_DAPM_MIXER("Headphone R", amp4), +}; + +static const struct snd_soc_dapm_widget cs42l43_b_widgets[] = { + SND_SOC_DAPM_AIF_OUT("DP3TX3", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP3TX4", NULL, 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX3", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX4", NULL, 3, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_PGA("Decimator 5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 6", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("ISRC1DEC3", CS42L43_ISRC1_CTRL, + CS42L43B_ISRC_DEC3_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1DEC4", CS42L43_ISRC1_CTRL, + CS42L43B_ISRC_DEC4_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2DEC3", CS42L43_ISRC2_CTRL, + CS42L43B_ISRC_DEC3_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2DEC4", CS42L43_ISRC2_CTRL, + CS42L43B_ISRC_DEC4_EN_SHIFT, 0, NULL, 0), + + CS42L43_DAPM_MUX("ASPTX1", b_asptx1), + CS42L43_DAPM_MUX("ASPTX2", b_asptx2), + CS42L43_DAPM_MUX("ASPTX3", b_asptx3), + CS42L43_DAPM_MUX("ASPTX4", b_asptx4), + CS42L43_DAPM_MUX("ASPTX5", b_asptx5), + CS42L43_DAPM_MUX("ASPTX6", b_asptx6), + + CS42L43_DAPM_MUX("DP1TX1", b_dp1tx1), + CS42L43_DAPM_MUX("DP1TX2", b_dp1tx2), + CS42L43_DAPM_MUX("DP1TX3", b_dp1tx3), + CS42L43_DAPM_MUX("DP1TX4", b_dp1tx4), + CS42L43_DAPM_MUX("DP2TX1", b_dp2tx1), + CS42L43_DAPM_MUX("DP2TX2", b_dp2tx2), + CS42L43_DAPM_MUX("DP3TX1", b_dp3tx1), + CS42L43_DAPM_MUX("DP3TX2", b_dp3tx2), + CS42L43_DAPM_MUX("DP3TX3", b_dp3tx3), + CS42L43_DAPM_MUX("DP3TX4", b_dp3tx4), + CS42L43_DAPM_MUX("DP4TX1", b_dp4tx1), + CS42L43_DAPM_MUX("DP4TX2", b_dp4tx2), + CS42L43_DAPM_MUX("DP4TX3", b_dp4tx3), + CS42L43_DAPM_MUX("DP4TX4", b_dp4tx4), + + CS42L43_DAPM_MUX("ASRC INT1", b_asrcint1), + CS42L43_DAPM_MUX("ASRC INT2", b_asrcint2), + CS42L43_DAPM_MUX("ASRC INT3", b_asrcint3), + CS42L43_DAPM_MUX("ASRC INT4", b_asrcint4), + CS42L43_DAPM_MUX("ASRC DEC1", b_asrcdec1), + CS42L43_DAPM_MUX("ASRC DEC2", b_asrcdec2), + CS42L43_DAPM_MUX("ASRC DEC3", b_asrcdec3), + CS42L43_DAPM_MUX("ASRC DEC4", b_asrcdec4), + + CS42L43_DAPM_MUX("ISRC1INT1", b_isrc1int1), + CS42L43_DAPM_MUX("ISRC1INT2", b_isrc1int2), + CS42L43_DAPM_MUX("ISRC1DEC1", b_isrc1dec1), + CS42L43_DAPM_MUX("ISRC1DEC2", b_isrc1dec2), + CS42L43_DAPM_MUX("ISRC1DEC3", b_isrc1dec3), + CS42L43_DAPM_MUX("ISRC1DEC4", b_isrc1dec4), + CS42L43_DAPM_MUX("ISRC2INT1", b_isrc2int1), + CS42L43_DAPM_MUX("ISRC2INT2", b_isrc2int2), + CS42L43_DAPM_MUX("ISRC2DEC1", b_isrc2dec1), + CS42L43_DAPM_MUX("ISRC2DEC2", b_isrc2dec2), + CS42L43_DAPM_MUX("ISRC2DEC3", b_isrc2dec3), + CS42L43_DAPM_MUX("ISRC2DEC4", b_isrc2dec4), + + CS42L43_DAPM_MUX("SPDIF1", b_spdif1), + CS42L43_DAPM_MUX("SPDIF2", b_spdif2), + + CS42L43_DAPM_MIXER("EQ1", b_eq1), + CS42L43_DAPM_MIXER("EQ2", b_eq2), + + CS42L43_DAPM_MIXER("Speaker L", b_amp1), + CS42L43_DAPM_MIXER("Speaker R", b_amp2), + + CS42L43_DAPM_MIXER("Headphone L", b_amp3), + CS42L43_DAPM_MIXER("Headphone R", b_amp4), +}; + +static const struct snd_soc_dapm_route cs42l43_a_routes[] = { + { "Decimator 1 Mode", "PDM", "PDM1L" }, + { "Decimator 1 Mode", "ADC", "ADC1" }, + { "Decimator 2 Mode", "PDM", "PDM1R" }, + { "Decimator 2 Mode", "ADC", "ADC2" }, + + { "Decimator 1", NULL, "Decimator 1 Mode" }, + { "Decimator 2", NULL, "Decimator 2 Mode" }, + { "Decimator 3", NULL, "PDM2L" }, + { "Decimator 4", NULL, "PDM2R" }, CS42L43_MUX_ROUTES("ASPTX1", "ASPTX1"), CS42L43_MUX_ROUTES("ASPTX2", "ASPTX2"), @@ -2110,6 +2486,81 @@ static const struct snd_soc_dapm_route cs42l43_routes[] = { CS42L43_MIXER_ROUTES("Headphone R", "HP"), }; +static const struct snd_soc_dapm_route cs42l43_b_routes[] = { + { "Decimator 1", NULL, "ADC1" }, + { "Decimator 2", NULL, "ADC2" }, + { "Decimator 3", NULL, "PDM1L" }, + { "Decimator 4", NULL, "PDM1R" }, + { "Decimator 5", NULL, "PDM2L" }, + { "Decimator 6", NULL, "PDM2R" }, + + { "DP3 Capture", NULL, "DP3TX3" }, + { "DP3 Capture", NULL, "DP3TX4" }, + { "DP4 Capture", NULL, "DP4TX3" }, + { "DP4 Capture", NULL, "DP4TX4" }, + + { "ISRC1DEC3", NULL, "ISRC1" }, + { "ISRC1DEC4", NULL, "ISRC1" }, + { "ISRC2DEC3", NULL, "ISRC2" }, + { "ISRC2DEC4", NULL, "ISRC2" }, + + CS42L43B_MUX_ROUTES("ASPTX1", "ASPTX1"), + CS42L43B_MUX_ROUTES("ASPTX2", "ASPTX2"), + CS42L43B_MUX_ROUTES("ASPTX3", "ASPTX3"), + CS42L43B_MUX_ROUTES("ASPTX4", "ASPTX4"), + CS42L43B_MUX_ROUTES("ASPTX5", "ASPTX5"), + CS42L43B_MUX_ROUTES("ASPTX6", "ASPTX6"), + + CS42L43B_MUX_ROUTES("DP1TX1", "DP1TX1"), + CS42L43B_MUX_ROUTES("DP1TX2", "DP1TX2"), + CS42L43B_MUX_ROUTES("DP1TX3", "DP1TX3"), + CS42L43B_MUX_ROUTES("DP1TX4", "DP1TX4"), + CS42L43B_MUX_ROUTES("DP2TX1", "DP2TX1"), + CS42L43B_MUX_ROUTES("DP2TX2", "DP2TX2"), + CS42L43B_MUX_ROUTES("DP3TX1", "DP3TX1"), + CS42L43B_MUX_ROUTES("DP3TX2", "DP3TX2"), + CS42L43B_MUX_ROUTES("DP3TX3", "DP3TX3"), + CS42L43B_MUX_ROUTES("DP3TX4", "DP3TX4"), + CS42L43B_MUX_ROUTES("DP4TX1", "DP4TX1"), + CS42L43B_MUX_ROUTES("DP4TX2", "DP4TX2"), + CS42L43B_MUX_ROUTES("DP4TX3", "DP4TX3"), + CS42L43B_MUX_ROUTES("DP4TX4", "DP4TX4"), + + CS42L43B_MUX_ROUTES("ASRC INT1", "ASRC_INT1"), + CS42L43B_MUX_ROUTES("ASRC INT2", "ASRC_INT2"), + CS42L43B_MUX_ROUTES("ASRC INT3", "ASRC_INT3"), + CS42L43B_MUX_ROUTES("ASRC INT4", "ASRC_INT4"), + CS42L43B_MUX_ROUTES("ASRC DEC1", "ASRC_DEC1"), + CS42L43B_MUX_ROUTES("ASRC DEC2", "ASRC_DEC2"), + CS42L43B_MUX_ROUTES("ASRC DEC3", "ASRC_DEC3"), + CS42L43B_MUX_ROUTES("ASRC DEC4", "ASRC_DEC4"), + + CS42L43B_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + CS42L43B_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + CS42L43B_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + CS42L43B_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + CS42L43B_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + CS42L43B_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + CS42L43B_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + CS42L43B_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + CS42L43B_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + CS42L43B_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + CS42L43B_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + CS42L43B_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + CS42L43B_MUX_ROUTES("SPDIF1", "SPDIF"), + CS42L43B_MUX_ROUTES("SPDIF2", "SPDIF"), + + CS42L43B_MIXER_ROUTES("EQ1", "EQ"), + CS42L43B_MIXER_ROUTES("EQ2", "EQ"), + + CS42L43B_MIXER_ROUTES("Speaker L", "AMP1"), + CS42L43B_MIXER_ROUTES("Speaker R", "AMP2"), + + CS42L43B_MIXER_ROUTES("Headphone L", "HP"), + CS42L43B_MIXER_ROUTES("Headphone R", "HP"), +}; + static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, int src, unsigned int freq, int dir) { @@ -2126,8 +2577,14 @@ static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, static int cs42l43_component_probe(struct snd_soc_component *component) { + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + unsigned int num_controls, num_widgets, num_routes; + const struct snd_soc_dapm_widget *widgets; + const struct snd_kcontrol_new *controls; + const struct snd_soc_dapm_route *routes; struct cs42l43 *cs42l43 = priv->core; + int ret; snd_soc_component_init_regmap(component, cs42l43->regmap); @@ -2139,6 +2596,39 @@ static int cs42l43_component_probe(struct snd_soc_component *component) priv->component = component; priv->constraint = cs42l43_constraint; + switch (cs42l43->variant_id) { + case CS42L43_DEVID_VAL: + controls = cs42l43_a_controls; + num_controls = ARRAY_SIZE(cs42l43_a_controls); + widgets = cs42l43_a_widgets; + num_widgets = ARRAY_SIZE(cs42l43_a_widgets); + routes = cs42l43_a_routes; + num_routes = ARRAY_SIZE(cs42l43_a_routes); + break; + case CS42L43B_DEVID_VAL: + controls = cs42l43_b_controls; + num_controls = ARRAY_SIZE(cs42l43_b_controls); + widgets = cs42l43_b_widgets; + num_widgets = ARRAY_SIZE(cs42l43_b_widgets); + routes = cs42l43_b_routes; + num_routes = ARRAY_SIZE(cs42l43_b_routes); + break; + default: + return -EINVAL; + } + + ret = snd_soc_add_component_controls(component, controls, num_controls); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, routes, num_routes); + if (ret) + return ret; + return 0; } diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index b2fa2cd1d99f..fd50feb26ae9 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -61,7 +61,7 @@ struct cs42l43_codec { unsigned int refclk_freq; struct completion pll_ready; - unsigned int decim_cache[4]; + unsigned int decim_cache[6]; unsigned int adc_ena; unsigned int hp_ena; @@ -103,7 +103,7 @@ struct cs42l43_codec { bool hp_ilimited; int hp_ilimit_count; - struct snd_kcontrol *kctl[5]; + struct snd_kcontrol *kctl[7]; }; #if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index 1e1307a16f81..e590a43559e4 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -357,8 +357,11 @@ struct cs42l84_pll_params { * Common PLL Settings for given BCLK */ static const struct cs42l84_pll_params pll_ratio_table[] = { + { 2822400, 1, 0, 0x40, 0x000000, 0x03, 0x10, 11289600}, { 3072000, 1, 0, 0x40, 0x000000, 0x03, 0x10, 12288000}, + { 5644800, 1, 0, 0x40, 0x000000, 0x03, 0x10, 11289600}, { 6144000, 1, 1, 0x40, 0x000000, 0x03, 0x10, 12288000}, + { 11289600, 0, 0, 0, 0, 0, 0, 11289600}, { 12288000, 0, 0, 0, 0, 0, 0, 12288000}, { 24576000, 1, 3, 0x40, 0x000000, 0x03, 0x10, 12288000}, }; @@ -408,11 +411,18 @@ static int cs42l84_pll_config(struct snd_soc_component *component) CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI, FIELD_PREP(CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI, fsync >> 7)); - /* Save what the MCLK will be */ + /* + * MCLK values are binned into 12 or 24 MHz regions. If MCLK is exactly + * 12 or 24 MHz, the high bit of CCM_CTL1_MCLK_F is set. If MCLK + * is in the region of 24 MHz, the low bit is set. This seemingly + * corresponds to CS42L42's documented INTERNAL_FS and MCLKDIV + * behaviour respectively. + */ switch (pll_ratio_table[i].mclk_int) { case 12000000: cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12MHZ; break; + case 11289600: case 12288000: cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12_288KHZ; break; @@ -670,14 +680,18 @@ static struct snd_soc_dai_driver cs42l84_dai = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, .formats = CS42L84_FORMATS, }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 1, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, .formats = CS42L84_FORMATS, }, .symmetric_rate = 1, diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c index 039b3ecb3b9b..0faca384073a 100644 --- a/sound/soc/codecs/cs42xx8-i2c.c +++ b/sound/soc/codecs/cs42xx8-i2c.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Cirrus Logic CS42448/CS42888 Audio CODEC DAI I2C driver * * Copyright (C) 2014 Freescale Semiconductor, Inc. * * Author: Nicolin Chen <Guangyu.Chen@freescale.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include <linux/i2c.h> diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index d14538c49b97..12fe9b3e2525 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver * * Copyright (C) 2014 Freescale Semiconductor, Inc. * * Author: Nicolin Chen <Guangyu.Chen@freescale.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include <linux/clk.h> @@ -43,6 +40,7 @@ struct cs42xx8_priv { struct clk *clk; bool slave_mode; + bool is_tdm_mode; unsigned long sysclk; u32 tx_channels; struct gpio_desc *gpiod_reset; @@ -217,6 +215,8 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); u32 val; + cs42xx8->is_tdm_mode = false; + /* Set DAI format */ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_LEFT_J: @@ -230,6 +230,7 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, break; case SND_SOC_DAIFMT_DSP_A: val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM; + cs42xx8->is_tdm_mode = true; break; default: dev_err(component->dev, "unsupported dai format\n"); @@ -253,6 +254,11 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } + if (cs42xx8->is_tdm_mode && !cs42xx8->slave_mode) { + dev_err(component->dev, "TDM mode is supported only in slave mode\n"); + return -EINVAL; + } + return 0; } @@ -335,6 +341,19 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, cs42xx8->rate[tx] = params_rate(params); + if (cs42xx8->is_tdm_mode) { + if (cs42xx8->sysclk < 256 * cs42xx8->rate[tx]) { + dev_err(component->dev, "Unsupported sysclk in TDM mode\n"); + return -EINVAL; + } + + if (!tx && cs42xx8->rate[tx] > 100000) { + dev_err(component->dev, + "ADC does not support Quad-Speed Mode in TDM mode\n"); + return -EINVAL; + } + } + mask = CS42XX8_FUNCMOD_MFREQ_MASK; val = cs42xx8_ratios[i].mfreq; diff --git a/sound/soc/codecs/cs42xx8.h b/sound/soc/codecs/cs42xx8.h index 342389e8b1a8..4087c91471d1 100644 --- a/sound/soc/codecs/cs42xx8.h +++ b/sound/soc/codecs/cs42xx8.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * cs42xx8.h - Cirrus Logic CS42448/CS42888 Audio CODEC driver header file * * Copyright (C) 2014 Freescale Semiconductor, Inc. * * Author: Nicolin Chen <Guangyu.Chen@freescale.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _CS42XX8_H diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index d9d932a78b71..0b3b108aaeee 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -924,6 +924,7 @@ static void da7219_aad_handle_gnd_switch_time(struct snd_soc_component *componen * Suspend/Resume */ +#ifdef CONFIG_PM void da7219_aad_suspend(struct snd_soc_component *component) { struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); @@ -981,7 +982,7 @@ void da7219_aad_resume(struct snd_soc_component *component) enable_irq(da7219_aad->irq); } - +#endif /* * Init/Exit diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h index fbfbf3e67918..ea5b81819247 100644 --- a/sound/soc/codecs/da7219-aad.h +++ b/sound/soc/codecs/da7219-aad.h @@ -209,8 +209,13 @@ struct da7219_aad_priv { void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack); /* Suspend/Resume */ +#ifdef CONFIG_PM void da7219_aad_suspend(struct snd_soc_component *component); void da7219_aad_resume(struct snd_soc_component *component); +#else +static inline void da7219_aad_suspend(struct snd_soc_component *component) { } +static inline void da7219_aad_resume(struct snd_soc_component *component) { } +#endif /* Init/Exit */ int da7219_aad_init(struct snd_soc_component *component); diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c index 39cebaa167be..8a7d26a08c03 100644 --- a/sound/soc/codecs/jz4725b.c +++ b/sound/soc/codecs/jz4725b.c @@ -160,7 +160,6 @@ enum { struct jz_icdc { struct regmap *regmap; void __iomem *base; - struct clk *clk; }; static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_adc_tlv, 0, 150, 0); @@ -405,8 +404,6 @@ static int jz4725b_codec_dev_probe(struct snd_soc_component *component) struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); struct regmap *map = icdc->regmap; - clk_prepare_enable(icdc->clk); - /* Write CONFIGn (n=1 to 8) bits. * The value 0x0f is specified in the datasheet as a requirement. */ @@ -418,16 +415,8 @@ static int jz4725b_codec_dev_probe(struct snd_soc_component *component) return 0; } -static void jz4725b_codec_dev_remove(struct snd_soc_component *component) -{ - struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); - - clk_disable_unprepare(icdc->clk); -} - static const struct snd_soc_component_driver jz4725b_codec = { .probe = jz4725b_codec_dev_probe, - .remove = jz4725b_codec_dev_remove, .set_bias_level = jz4725b_codec_set_bias_level, .controls = jz4725b_codec_controls, .num_controls = ARRAY_SIZE(jz4725b_codec_controls), @@ -618,6 +607,7 @@ static int jz4725b_codec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct jz_icdc *icdc; + struct clk *clk; int ret; icdc = devm_kzalloc(dev, sizeof(*icdc), GFP_KERNEL); @@ -633,9 +623,9 @@ static int jz4725b_codec_probe(struct platform_device *pdev) if (IS_ERR(icdc->regmap)) return PTR_ERR(icdc->regmap); - icdc->clk = devm_clk_get(&pdev->dev, "aic"); - if (IS_ERR(icdc->clk)) - return PTR_ERR(icdc->clk); + clk = devm_clk_get_enabled(dev, "aic"); + if (IS_ERR(clk)) + return PTR_ERR(clk); platform_set_drvdata(pdev, icdc); diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c index 344c251be397..6846ace06415 100644 --- a/sound/soc/codecs/jz4760.c +++ b/sound/soc/codecs/jz4760.c @@ -163,7 +163,6 @@ struct jz_codec { struct device *dev; struct regmap *regmap; void __iomem *base; - struct clk *clk; }; static int jz4760_codec_set_bias_level(struct snd_soc_component *codec, @@ -602,25 +601,13 @@ static void jz4760_codec_codec_init_regs(struct snd_soc_component *codec) static int jz4760_codec_codec_probe(struct snd_soc_component *codec) { - struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); - - clk_prepare_enable(jz_codec->clk); - jz4760_codec_codec_init_regs(codec); return 0; } -static void jz4760_codec_codec_remove(struct snd_soc_component *codec) -{ - struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); - - clk_disable_unprepare(jz_codec->clk); -} - static const struct snd_soc_component_driver jz4760_codec_soc_codec_dev = { .probe = jz4760_codec_codec_probe, - .remove = jz4760_codec_codec_remove, .set_bias_level = jz4760_codec_set_bias_level, .controls = jz4760_codec_snd_controls, .num_controls = ARRAY_SIZE(jz4760_codec_snd_controls), @@ -818,6 +805,7 @@ static int jz4760_codec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct jz_codec *codec; + struct clk *clk; int ret; codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL); @@ -835,9 +823,9 @@ static int jz4760_codec_probe(struct platform_device *pdev) if (IS_ERR(codec->regmap)) return PTR_ERR(codec->regmap); - codec->clk = devm_clk_get(dev, "aic"); - if (IS_ERR(codec->clk)) - return PTR_ERR(codec->clk); + clk = devm_clk_get_enabled(dev, "aic"); + if (IS_ERR(clk)) + return PTR_ERR(clk); platform_set_drvdata(pdev, codec); diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c index 6b86d47028d7..be1ecdcc737b 100644 --- a/sound/soc/codecs/jz4770.c +++ b/sound/soc/codecs/jz4770.c @@ -179,7 +179,6 @@ struct jz_codec { struct device *dev; struct regmap *regmap; void __iomem *base; - struct clk *clk; }; static int jz4770_codec_set_bias_level(struct snd_soc_component *codec, @@ -634,25 +633,13 @@ static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec) static int jz4770_codec_codec_probe(struct snd_soc_component *codec) { - struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); - - clk_prepare_enable(jz_codec->clk); - jz4770_codec_codec_init_regs(codec); return 0; } -static void jz4770_codec_codec_remove(struct snd_soc_component *codec) -{ - struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); - - clk_disable_unprepare(jz_codec->clk); -} - static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = { .probe = jz4770_codec_codec_probe, - .remove = jz4770_codec_codec_remove, .set_bias_level = jz4770_codec_set_bias_level, .controls = jz4770_codec_snd_controls, .num_controls = ARRAY_SIZE(jz4770_codec_snd_controls), @@ -865,6 +852,7 @@ static int jz4770_codec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct jz_codec *codec; + struct clk *clk; int ret; codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL); @@ -882,9 +870,9 @@ static int jz4770_codec_probe(struct platform_device *pdev) if (IS_ERR(codec->regmap)) return PTR_ERR(codec->regmap); - codec->clk = devm_clk_get(dev, "aic"); - if (IS_ERR(codec->clk)) - return PTR_ERR(codec->clk); + clk = devm_clk_get_enabled(dev, "aic"); + if (IS_ERR(clk)) + return PTR_ERR(clk); platform_set_drvdata(pdev, codec); diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index ff58805e97d1..65f095c47191 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -1015,10 +1015,8 @@ static int max98390_i2c_probe(struct i2c_client *i2c) struct i2c_adapter *adapter = i2c->adapter; struct gpio_desc *reset_gpio; - ret = i2c_check_functionality(adapter, - I2C_FUNC_SMBUS_BYTE - | I2C_FUNC_SMBUS_BYTE_DATA); - if (!ret) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&i2c->dev, "I2C check functionality failed\n"); return -ENXIO; } diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c index 2bf8976c1828..5bc3d95ade5a 100644 --- a/sound/soc/codecs/max98520.c +++ b/sound/soc/codecs/max98520.c @@ -681,8 +681,8 @@ static int max98520_i2c_probe(struct i2c_client *i2c) struct max98520_priv *max98520; struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent); - ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA); - if (!ret) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&i2c->dev, "I2C check functionality failed\n"); return -ENXIO; } diff --git a/sound/soc/codecs/nau8315.c b/sound/soc/codecs/nau8315.c index 125742601f88..9ac2048ff0b0 100644 --- a/sound/soc/codecs/nau8315.c +++ b/sound/soc/codecs/nau8315.c @@ -11,7 +11,6 @@ #include <linux/acpi.h> #include <linux/device.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index f1ee42af264b..2d5163c15d0d 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -517,18 +517,21 @@ static const struct snd_kcontrol_new peb2466_ch3_out_mix_controls[] = { SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(3), 0, 1, 0) }; +static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(peb2466_gain_p_tlv, -600, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(peb2466_gain_c_tlv, 0, 600); + static const struct snd_kcontrol_new peb2466_controls[] = { /* Attenuators */ - SOC_SINGLE("DAC0 -6dB Playback Switch", PEB2466_CR3(0), 2, 1, 0), - SOC_SINGLE("DAC1 -6dB Playback Switch", PEB2466_CR3(1), 2, 1, 0), - SOC_SINGLE("DAC2 -6dB Playback Switch", PEB2466_CR3(2), 2, 1, 0), - SOC_SINGLE("DAC3 -6dB Playback Switch", PEB2466_CR3(3), 2, 1, 0), + SOC_SINGLE_TLV("DAC0 -6dB Playback Volume", PEB2466_CR3(0), 2, 1, 1, peb2466_gain_p_tlv), + SOC_SINGLE_TLV("DAC1 -6dB Playback Volume", PEB2466_CR3(1), 2, 1, 1, peb2466_gain_p_tlv), + SOC_SINGLE_TLV("DAC2 -6dB Playback Volume", PEB2466_CR3(2), 2, 1, 1, peb2466_gain_p_tlv), + SOC_SINGLE_TLV("DAC3 -6dB Playback Volume", PEB2466_CR3(3), 2, 1, 1, peb2466_gain_p_tlv), /* Amplifiers */ - SOC_SINGLE("ADC0 +6dB Capture Switch", PEB2466_CR3(0), 3, 1, 0), - SOC_SINGLE("ADC1 +6dB Capture Switch", PEB2466_CR3(1), 3, 1, 0), - SOC_SINGLE("ADC2 +6dB Capture Switch", PEB2466_CR3(2), 3, 1, 0), - SOC_SINGLE("ADC3 +6dB Capture Switch", PEB2466_CR3(3), 3, 1, 0), + SOC_SINGLE_TLV("ADC0 +6dB Capture Volume", PEB2466_CR3(0), 3, 1, 0, peb2466_gain_c_tlv), + SOC_SINGLE_TLV("ADC1 +6dB Capture Volume", PEB2466_CR3(1), 3, 1, 0, peb2466_gain_c_tlv), + SOC_SINGLE_TLV("ADC2 +6dB Capture Volume", PEB2466_CR3(2), 3, 1, 0, peb2466_gain_c_tlv), + SOC_SINGLE_TLV("ADC3 +6dB Capture Volume", PEB2466_CR3(3), 3, 1, 0, peb2466_gain_c_tlv), /* Tone generators */ SOC_ENUM_EXT("DAC0 TG1 Freq", peb2466_tg_freq[0][0], diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c index 01c58b15fd91..a80643099644 100644 --- a/sound/soc/codecs/rt1318.c +++ b/sound/soc/codecs/rt1318.c @@ -18,7 +18,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/firmware.h> -#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 8bb7e8497c72..b0aeeab26bd9 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1013,13 +1013,34 @@ static void rt1320_set_advancemode(struct rt1320_sdw_priv *rt1320) struct device *dev = &rt1320->sdw_slave->dev; struct rt1320_datafixpoint r0_data[2]; unsigned short l_advancegain, r_advancegain; + FwPara_Get_HwSwGain audDriverDataHwSwGain = {0}; + unsigned int HwAdvGain = 0; int ret; + /* Get new hardware advance gain by ID 1300 */ + ret = rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 1300, + &audDriverDataHwSwGain, sizeof(audDriverDataHwSwGain)); + if (ret == 0) { + HwAdvGain = audDriverDataHwSwGain.HwAdvGain; + dev_dbg(dev, "%s, HwAdvGain=%d\n", __func__, HwAdvGain); + dev_dbg(dev, "%s, HwBasGain=%d\n", __func__, audDriverDataHwSwGain.HwBasGain); + dev_dbg(dev, "%s, SwAdvGain=%d\n", __func__, audDriverDataHwSwGain.SwAdvGain); + dev_dbg(dev, "%s, SwBasGain=%d\n", __func__, audDriverDataHwSwGain.SwBasGain); + } else { + dev_dbg(dev, "%s: param 1300 not supported, ret=%d\n", __func__, ret); + } + /* Get advance gain/r0 */ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint)); rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint)); - l_advancegain = r0_data[0].advancegain; - r_advancegain = r0_data[1].advancegain; + + if (HwAdvGain != 0) { + l_advancegain = HwAdvGain & 0xffff; + r_advancegain = (HwAdvGain >> 16) & 0xffff; + } else { + l_advancegain = r0_data[0].advancegain; + r_advancegain = r0_data[1].advancegain; + } dev_dbg(dev, "%s, LR advanceGain=0x%x 0x%x\n", __func__, l_advancegain, r_advancegain); /* set R0 and enable protection by SetParameter id 6, 7 */ @@ -2486,6 +2507,45 @@ static int rt1320_rae_update_put(struct snd_kcontrol *kcontrol, return 0; } +static int rt1320_brown_out_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + int ret, changed = 0; + + if (!rt1320->hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + if (rt1320->brown_out != ucontrol->value.integer.value[0]) { + changed = 1; + rt1320->brown_out = ucontrol->value.integer.value[0]; + } + + if (rt1320->brown_out == 0) + regmap_write(rt1320->regmap, 0xdb03, 0x00); + else + regmap_write(rt1320->regmap, 0xdb03, 0xf0); + + + return changed; +} + +static int rt1320_brown_out_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1320->brown_out; + + return 0; +} + static int rt1320_r0_temperature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2545,6 +2605,8 @@ static const struct snd_kcontrol_new rt1320_snd_controls[] = { rt1320_r0_temperature_get, rt1320_r0_temperature_put), SOC_SINGLE_EXT("RAE Update", SND_SOC_NOPM, 0, 1, 0, rt1320_rae_update_get, rt1320_rae_update_put), + SOC_SINGLE_EXT("Brown Out Switch", SND_SOC_NOPM, 0, 1, 0, + rt1320_brown_out_get, rt1320_brown_out_put), }; static const struct snd_kcontrol_new rt1320_spk_l_dac = @@ -2904,6 +2966,7 @@ static int rt1320_sdw_init(struct device *dev, struct regmap *regmap, rt1320->fu_dapm_mute = true; rt1320->fu_mixer_mute[0] = rt1320->fu_mixer_mute[1] = rt1320->fu_mixer_mute[2] = rt1320->fu_mixer_mute[3] = true; + rt1320->brown_out = 1; INIT_WORK(&rt1320->load_dspfw_work, rt1320_load_dspfw_work); diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index 5a9f496dd848..a7b573883dd0 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -121,6 +121,19 @@ struct rt1320_datafixpoint { int invrs; }; +typedef struct FwPara_HwSwGain { + unsigned int SwAdvGain; + unsigned int SwBasGain; + unsigned int HwAdvGain; + unsigned int HwBasGain; + unsigned int reserve0; + unsigned int reserve1; + unsigned int reserve2; + unsigned int reserve3; + unsigned int reserve4; + unsigned int reserve5; +} __attribute__((packed)) FwPara_Get_HwSwGain; + struct rt1320_paramcmd { unsigned char moudleid; unsigned char commandtype; @@ -159,6 +172,7 @@ struct rt1320_sdw_priv { bool hw_init; bool first_hw_init; int version_id; + int brown_out; unsigned int dev_id; bool fu_dapm_mute; bool fu_mixer_mute[4]; diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 54d84581ec47..199507d12841 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -303,7 +303,7 @@ static const struct snd_soc_component_driver rt5514_spi_component = { .hw_params = rt5514_spi_hw_params, .hw_free = rt5514_spi_hw_free, .pointer = rt5514_spi_pcm_pointer, - .pcm_construct = rt5514_spi_pcm_new, + .pcm_new = rt5514_spi_pcm_new, .legacy_dai_naming = 1, }; diff --git a/sound/soc/codecs/rt5575.c b/sound/soc/codecs/rt5575.c index c5525ad195ee..24e41af29689 100644 --- a/sound/soc/codecs/rt5575.c +++ b/sound/soc/codecs/rt5575.c @@ -339,7 +339,6 @@ MODULE_DEVICE_TABLE(of, rt5575_of_match); static struct i2c_driver rt5575_i2c_driver = { .driver = { .name = "rt5575", - .owner = THIS_MODULE, .of_match_table = rt5575_of_match, }, .probe = rt5575_i2c_probe, diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index db2222e6f2e7..f6c6294e1588 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1838,6 +1838,11 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, unsigned int pll_bit = 0; int ret; + if (!freq) { + rt5640->sysclk = 0; + return 0; + } + switch (clk_id) { case RT5640_SCLK_S_MCLK: ret = clk_set_rate(rt5640->mclk, freq); diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index 885edcf0a3a5..1bcafd5f4468 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -404,7 +404,7 @@ static const struct snd_soc_component_driver rt5677_spi_dai_component = { .hw_free = rt5677_spi_hw_free, .prepare = rt5677_spi_prepare, .pointer = rt5677_spi_pcm_pointer, - .pcm_construct = rt5677_spi_pcm_new, + .pcm_new = rt5677_spi_pcm_new, .legacy_dai_naming = 1, }; @@ -624,12 +624,19 @@ static const struct acpi_device_id rt5677_spi_acpi_id[] = { MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id); #endif +static const struct spi_device_id rt5677_spi_ids[] = { + { "rt5677", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, rt5677_spi_ids); + static struct spi_driver rt5677_spi_driver = { .driver = { .name = DRV_NAME, .acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id), }, .probe = rt5677_spi_probe, + .id_table = rt5677_spi_ids, }; module_spi_driver(rt5677_spi_driver); diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index d46385249867..60a93c3fe2e7 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -5204,10 +5204,17 @@ MODULE_DEVICE_TABLE(of, rt5677_of_match); static const struct acpi_device_id rt5677_acpi_match[] = { { "10EC5677", RT5677 }, { "RT5677CE", RT5677 }, + { "10EC5677", RT5677 }, { } }; MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match); +static const struct i2c_device_id rt5677_i2c_id[] = { + { "rt5677", RT5677 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); + static void rt5677_read_device_properties(struct rt5677_priv *rt5677, struct device *dev) { @@ -5529,9 +5536,17 @@ static int rt5677_init_irq(struct i2c_client *i2c) return ret; } +static const struct acpi_gpio_params rt5677_acpi_reset_gpios = {0, 0, true}; +static const struct acpi_gpio_params rt5677_acpi_ldo2_gpios = {1, 0, false}; + +static const struct acpi_gpio_mapping rt5677_acpi_gpios[] = { + { "realtek,reset-gpios", &rt5677_acpi_reset_gpios, 1 }, + { "realtek,pow-ldo2-gpios", &rt5677_acpi_ldo2_gpios, 1 }, + {}, +}; + static int rt5677_i2c_probe(struct i2c_client *i2c) { - struct device *dev = &i2c->dev; struct rt5677_priv *rt5677; int ret; unsigned int val; @@ -5546,10 +5561,13 @@ static int rt5677_i2c_probe(struct i2c_client *i2c) INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work); i2c_set_clientdata(i2c, rt5677); - rt5677->type = (enum rt5677_type)(uintptr_t)device_get_match_data(dev); + rt5677->type = (enum rt5677_type)(uintptr_t)i2c_get_match_data(i2c); if (rt5677->type == 0) return -EINVAL; + if (devm_acpi_dev_add_driver_gpios(rt5677->dev, rt5677_acpi_gpios)) + dev_warn(rt5677->dev, "Unable to add GPIO mapping table\n"); + rt5677_read_device_properties(rt5677, &i2c->dev); /* pow-ldo2 and reset are optional. The codec pins may be statically @@ -5563,14 +5581,21 @@ static int rt5677_i2c_probe(struct i2c_client *i2c) dev_err(&i2c->dev, "Failed to request POW_LDO2: %d\n", ret); return ret; } + rt5677->reset_pin = devm_gpiod_get_optional(&i2c->dev, - "realtek,reset", GPIOD_OUT_LOW); + "realtek,reset", GPIOD_OUT_HIGH); + if (IS_ERR(rt5677->reset_pin)) { ret = PTR_ERR(rt5677->reset_pin); dev_err(&i2c->dev, "Failed to request RESET: %d\n", ret); return ret; } + if (rt5677->reset_pin) { + msleep(1); + gpiod_set_value_cansleep(rt5677->reset_pin, 0); + } + if (rt5677->pow_ldo2 || rt5677->reset_pin) { /* Wait a while until I2C bus becomes available. The datasheet * does not specify the exact we should wait but startup @@ -5596,7 +5621,13 @@ static int rt5677_i2c_probe(struct i2c_client *i2c) return ret; } - regmap_read(rt5677->regmap, RT5677_VENDOR_ID2, &val); + ret = regmap_read(rt5677->regmap, RT5677_VENDOR_ID2, &val); + if (ret) { + dev_err(&i2c->dev, + "Failed to read ID register: %d\n", ret); + return -ENODEV; + } + if (val != RT5677_DEVICE_ID) { dev_err(&i2c->dev, "Device with ID register %#x is not rt5677\n", val); @@ -5665,6 +5696,7 @@ static struct i2c_driver rt5677_i2c_driver = { .of_match_table = rt5677_of_match, .acpi_match_table = rt5677_acpi_match, }, + .id_table = rt5677_i2c_id, .probe = rt5677_i2c_probe, .remove = rt5677_i2c_remove, }; diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index d67ebae067d9..876f7674015c 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -421,7 +421,7 @@ #define RT5677_DAC3_R_VOL_MASK (0xff) #define RT5677_DAC3_R_VOL_SFT 0 -/* DAC3 Digital Volume (0x19) */ +/* DAC1 Digital Volume (0x19) */ #define RT5677_DAC1_L_VOL_MASK (0xff << 8) #define RT5677_DAC1_L_VOL_SFT 8 #define RT5677_DAC1_R_VOL_MASK (0xff) diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 8c3b2652b02e..946aa6a4e57c 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ASoC codec driver for spear platform * @@ -5,10 +6,6 @@ * * Copyright (C) 2012 ST Microelectronics * Rajeev Kumar <rajeevkumar.linux@gmail.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 43449d7c2584..80206c2e0946 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -487,7 +487,7 @@ static int tas2552_runtime_suspend(struct device *dev) regcache_cache_only(tas2552->regmap, true); regcache_mark_dirty(tas2552->regmap); - gpiod_set_value(tas2552->enable_gpio, 0); + gpiod_set_value_cansleep(tas2552->enable_gpio, 0); return 0; } @@ -496,7 +496,7 @@ static int tas2552_runtime_resume(struct device *dev) { struct tas2552_data *tas2552 = dev_get_drvdata(dev); - gpiod_set_value(tas2552->enable_gpio, 1); + gpiod_set_value_cansleep(tas2552->enable_gpio, 1); tas2552_sw_shutdown(tas2552, 0); @@ -583,7 +583,7 @@ static int tas2552_component_probe(struct snd_soc_component *component) return ret; } - gpiod_set_value(tas2552->enable_gpio, 1); + gpiod_set_value_cansleep(tas2552->enable_gpio, 1); ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { @@ -608,7 +608,7 @@ static int tas2552_component_probe(struct snd_soc_component *component) probe_fail: pm_runtime_put_noidle(component->dev); - gpiod_set_value(tas2552->enable_gpio, 0); + gpiod_set_value_cansleep(tas2552->enable_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), tas2552->supplies); @@ -621,7 +621,7 @@ static void tas2552_component_remove(struct snd_soc_component *component) pm_runtime_put(component->dev); - gpiod_set_value(tas2552->enable_gpio, 0); + gpiod_set_value_cansleep(tas2552->enable_gpio, 0); }; #ifdef CONFIG_PM diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 36e25e48b354..423b7073b302 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -44,6 +44,11 @@ struct tas2764_priv { bool dac_powered; bool unmuted; + + struct { + int tx_mode; + unsigned int tx_mask; + } idle_slot_config; }; #include "tas2764-quirks.h" @@ -509,11 +514,101 @@ static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static int tas2764_write_sdout_idle_mask(struct tas2764_priv *tas2764, u32 mask) +{ + struct snd_soc_component *component = tas2764->component; + int i, ret; + + /* Hardware supports up to 64 slots, but we don't */ + for (i = 0; i < 4; i++) { + ret = snd_soc_component_write(component, + TAS2764_SDOUT_HIZ_1 + i, + (mask >> (i * 8)) & 0xff); + if (ret < 0) + return ret; + } + + return 0; +} + +static int tas2764_set_dai_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode) +{ + struct snd_soc_component *component = dai->component; + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + /* We don't support setting anything on SDIN */ + if (rx_mode) + return -EOPNOTSUPP; + + if (tas2764->idle_slot_config.tx_mask == tx_mask && + tas2764->idle_slot_config.tx_mode == tx_mode) + return 0; + + switch (tx_mode) { + case SND_SOC_DAI_TDM_IDLE_ZERO: + if (!tx_mask) + return -EINVAL; + + ret = tas2764_write_sdout_idle_mask(tas2764, tx_mask); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, + TAS2764_SDOUT_HIZ_9, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN); + if (ret < 0) + return ret; + + tas2764->idle_slot_config.tx_mask = tx_mask; + tas2764->idle_slot_config.tx_mode = tx_mode; + break; + case SND_SOC_DAI_TDM_IDLE_HIZ: + case SND_SOC_DAI_TDM_IDLE_OFF: + /* HiZ mode does not support a slot mask */ + ret = tas2764_write_sdout_idle_mask(tas2764, 0); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, + TAS2764_SDOUT_HIZ_9, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN, 0); + if (ret < 0) + return ret; + + tas2764->idle_slot_config.tx_mask = 0; + tas2764->idle_slot_config.tx_mode = tx_mode; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* The SDOUT idle slot mask must be cropped based on the BCLK ratio */ +static int tas2764_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(dai->component); + + if (!tas2764->idle_slot_config.tx_mask) + return 0; + + tas2764->idle_slot_config.tx_mask &= GENMASK((ratio / 8) - 1, 0); + + return tas2764_write_sdout_idle_mask(tas2764, tas2764->idle_slot_config.tx_mask); +} + static const struct snd_soc_dai_ops tas2764_dai_ops = { .mute_stream = tas2764_mute, .hw_params = tas2764_hw_params, .set_fmt = tas2764_set_fmt, + .set_bclk_ratio = tas2764_set_bclk_ratio, .set_tdm_slot = tas2764_set_dai_tdm_slot, + .set_tdm_idle = tas2764_set_dai_tdm_idle, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 538290ed3d92..4494bc4889dc 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -126,4 +126,15 @@ #define TAS2764_BOP_CFG0 TAS2764_REG(0X0, 0x1d) +#define TAS2764_SDOUT_HIZ_1 TAS2764_REG(0x1, 0x3d) +#define TAS2764_SDOUT_HIZ_2 TAS2764_REG(0x1, 0x3e) +#define TAS2764_SDOUT_HIZ_3 TAS2764_REG(0x1, 0x3f) +#define TAS2764_SDOUT_HIZ_4 TAS2764_REG(0x1, 0x40) +#define TAS2764_SDOUT_HIZ_5 TAS2764_REG(0x1, 0x41) +#define TAS2764_SDOUT_HIZ_6 TAS2764_REG(0x1, 0x42) +#define TAS2764_SDOUT_HIZ_7 TAS2764_REG(0x1, 0x43) +#define TAS2764_SDOUT_HIZ_8 TAS2764_REG(0x1, 0x44) +#define TAS2764_SDOUT_HIZ_9 TAS2764_REG(0x1, 0x45) +#define TAS2764_SDOUT_HIZ_9_FORCE_0_EN BIT(7) + #endif /* __TAS2764__ */ diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 6f878b01716f..d4d7d056141b 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -492,11 +492,86 @@ static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static int tas2770_set_dai_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int tx_mode, int rx_mode) +{ + struct snd_soc_component *component = dai->component; + struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + int ret; + + /* We don't support setting anything for SDIN */ + if (rx_mode) + return -EOPNOTSUPP; + + if (tas2770->idle_tx_mode == tx_mode) + return 0; + + switch (tx_mode) { + case SND_SOC_DAI_TDM_IDLE_PULLDOWN: + ret = snd_soc_component_update_bits(component, TAS2770_DIN_PD, + TAS2770_DIN_PD_SDOUT, + TAS2770_DIN_PD_SDOUT); + if (ret) + return ret; + + break; + case SND_SOC_DAI_TDM_IDLE_ZERO: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_KEEPER, + TAS2770_TDM_CFG_REG4_TX_KEEPER); + if (ret) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_FILL, 0); + if (ret) + return ret; + + break; + case SND_SOC_DAI_TDM_IDLE_HIZ: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_KEEPER, + TAS2770_TDM_CFG_REG4_TX_KEEPER); + if (ret) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_FILL, + TAS2770_TDM_CFG_REG4_TX_FILL); + if (ret) + return ret; + + break; + case SND_SOC_DAI_TDM_IDLE_OFF: + ret = snd_soc_component_update_bits(component, TAS2770_DIN_PD, + TAS2770_DIN_PD_SDOUT, 0); + if (ret) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_KEEPER, 0); + if (ret) + return ret; + + break; + + default: + return -EOPNOTSUPP; + } + + tas2770->idle_tx_mode = tx_mode; + + return 0; +} + static const struct snd_soc_dai_ops tas2770_dai_ops = { .mute_stream = tas2770_mute, .hw_params = tas2770_hw_params, .set_fmt = tas2770_set_fmt, .set_tdm_slot = tas2770_set_dai_tdm_slot, + .set_tdm_idle = tas2770_set_dai_tdm_idle, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index 3fd2e7003c50..102040b6bdf8 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -67,6 +67,14 @@ #define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4 #define TAS2770_TDM_CFG_REG3_30_MASK GENMASK(3, 0) #define TAS2770_TDM_CFG_REG3_30_SHIFT 0 + /* TDM Configuration Reg4 */ +#define TAS2770_TDM_CFG_REG4 TAS2770_REG(0X0, 0x0E) +#define TAS2770_TDM_CFG_REG4_TX_LSB_CFG BIT(7) +#define TAS2770_TDM_CFG_REG4_TX_KEEPER_CFG BIT(6) +#define TAS2770_TDM_CFG_REG4_TX_KEEPER BIT(5) +#define TAS2770_TDM_CFG_REG4_TX_FILL BIT(4) +#define TAS2770_TDM_CFG_REG4_TX_OFFSET_MASK GENMASK(3, 1) +#define TAS2770_TDM_CFG_REG4_TX_EDGE_FALLING BIT(0) /* TDM Configuration Reg5 */ #define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F) #define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6) @@ -115,6 +123,9 @@ #define TAS2770_TEMP_LSB TAS2770_REG(0X0, 0x2A) /* Interrupt Configuration */ #define TAS2770_INT_CFG TAS2770_REG(0X0, 0x30) + /* Data In Pull-Down */ +#define TAS2770_DIN_PD TAS2770_REG(0X0, 0x31) +#define TAS2770_DIN_PD_SDOUT BIT(7) /* Misc IRQ */ #define TAS2770_MISC_IRQ TAS2770_REG(0X0, 0x32) /* Clock Configuration */ @@ -146,6 +157,7 @@ struct tas2770_priv { int pdm_slot; bool dac_powered; bool unmuted; + int idle_tx_mode; }; #endif /* __TAS2770__ */ diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 41b89fcc69c3..c593f9da0c5b 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -121,29 +121,28 @@ static const struct i2c_device_id tasdevice_id[] = { { "tas5830", TAS5830 }, {} }; -MODULE_DEVICE_TABLE(i2c, tasdevice_id); #ifdef CONFIG_OF static const struct of_device_id tasdevice_of_match[] = { - { .compatible = "ti,tas2020" }, - { .compatible = "ti,tas2118" }, - { .compatible = "ti,tas2120" }, - { .compatible = "ti,tas2320" }, - { .compatible = "ti,tas2563" }, - { .compatible = "ti,tas2568" }, - { .compatible = "ti,tas2570" }, - { .compatible = "ti,tas2572" }, - { .compatible = "ti,tas2574" }, - { .compatible = "ti,tas2781" }, - { .compatible = "ti,tas5802" }, - { .compatible = "ti,tas5806m" }, - { .compatible = "ti,tas5806md" }, - { .compatible = "ti,tas5815" }, - { .compatible = "ti,tas5822" }, - { .compatible = "ti,tas5825" }, - { .compatible = "ti,tas5827" }, - { .compatible = "ti,tas5828" }, - { .compatible = "ti,tas5830" }, + { .compatible = "ti,tas2020", .data = &tasdevice_id[TAS2020] }, + { .compatible = "ti,tas2118", .data = &tasdevice_id[TAS2118] }, + { .compatible = "ti,tas2120", .data = &tasdevice_id[TAS2120] }, + { .compatible = "ti,tas2320", .data = &tasdevice_id[TAS2320] }, + { .compatible = "ti,tas2563", .data = &tasdevice_id[TAS2563] }, + { .compatible = "ti,tas2568", .data = &tasdevice_id[TAS2568] }, + { .compatible = "ti,tas2570", .data = &tasdevice_id[TAS2570] }, + { .compatible = "ti,tas2572", .data = &tasdevice_id[TAS2572] }, + { .compatible = "ti,tas2574", .data = &tasdevice_id[TAS2574] }, + { .compatible = "ti,tas2781", .data = &tasdevice_id[TAS2781] }, + { .compatible = "ti,tas5802", .data = &tasdevice_id[TAS5802] }, + { .compatible = "ti,tas5806m", .data = &tasdevice_id[TAS5806M] }, + { .compatible = "ti,tas5806md", .data = &tasdevice_id[TAS5806MD] }, + { .compatible = "ti,tas5815", .data = &tasdevice_id[TAS5815] }, + { .compatible = "ti,tas5822", .data = &tasdevice_id[TAS5822] }, + { .compatible = "ti,tas5825", .data = &tasdevice_id[TAS5825] }, + { .compatible = "ti,tas5827", .data = &tasdevice_id[TAS5827] }, + { .compatible = "ti,tas5828", .data = &tasdevice_id[TAS5828] }, + { .compatible = "ti,tas5830", .data = &tasdevice_id[TAS5830] }, {}, }; MODULE_DEVICE_TABLE(of, tasdevice_of_match); @@ -2023,15 +2022,12 @@ static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) if (IS_ERR(tas_priv->reset)) dev_err(tas_priv->dev, "%s Can't get reset GPIO\n", __func__); - - strscpy(tas_priv->dev_name, tasdevice_id[tas_priv->chip_id].name, - sizeof(tas_priv->dev_name)); } static int tasdevice_i2c_probe(struct i2c_client *i2c) { - const struct acpi_device_id *acpi_id; struct tasdevice_priv *tas_priv; + struct i2c_device_id *id_data; int ret; tas_priv = tasdevice_kzalloc(i2c); @@ -2041,20 +2037,23 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c) dev_set_drvdata(&i2c->dev, tas_priv); if (ACPI_HANDLE(&i2c->dev)) { - acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table, - &i2c->dev); - if (!acpi_id) { - dev_err(&i2c->dev, "No driver data\n"); - ret = -EINVAL; - goto err; - } - tas_priv->chip_id = acpi_id->driver_data; + id_data = (struct i2c_device_id *) + acpi_device_get_match_data(&i2c->dev); tas_priv->isacpi = true; } else { - tas_priv->chip_id = (uintptr_t)i2c_get_match_data(i2c); + id_data = (struct i2c_device_id *)i2c_get_match_data(i2c); tas_priv->isacpi = false; } + if (!id_data) { + dev_err(&i2c->dev, "No driver data\n"); + ret = -EINVAL; + goto err; + } + + tas_priv->chip_id = (uintptr_t)id_data->driver_data; + strscpy(tas_priv->dev_name, id_data->name, sizeof(tas_priv->dev_name)); + tasdevice_parse_dt(tas_priv); ret = tasdevice_init(tas_priv); @@ -2086,25 +2085,25 @@ static void tasdevice_i2c_remove(struct i2c_client *client) #ifdef CONFIG_ACPI static const struct acpi_device_id tasdevice_acpi_match[] = { - { "TXNW2020", TAS2020 }, - { "TXNW2118", TAS2118 }, - { "TXNW2120", TAS2120 }, - { "TXNW2320", TAS2320 }, - { "TXNW2563", TAS2563 }, - { "TXNW2568", TAS2568 }, - { "TXNW2570", TAS2570 }, - { "TXNW2572", TAS2572 }, - { "TXNW2574", TAS2574 }, - { "TXNW2781", TAS2781 }, - { "TXNW5802", TAS5802 }, - { "TXNW806M", TAS5806M }, - { "TXNW806D", TAS5806MD }, - { "TXNW5815", TAS5815 }, - { "TXNW5822", TAS5822 }, - { "TXNW5825", TAS5825 }, - { "TXNW5827", TAS5827 }, - { "TXNW5828", TAS5828 }, - { "TXNW5830", TAS5830 }, + { "TXNW2020", (kernel_ulong_t)&tasdevice_id[TAS2020] }, + { "TXNW2118", (kernel_ulong_t)&tasdevice_id[TAS2118] }, + { "TXNW2120", (kernel_ulong_t)&tasdevice_id[TAS2120] }, + { "TXNW2320", (kernel_ulong_t)&tasdevice_id[TAS2320] }, + { "TXNW2563", (kernel_ulong_t)&tasdevice_id[TAS2563] }, + { "TXNW2568", (kernel_ulong_t)&tasdevice_id[TAS2568] }, + { "TXNW2570", (kernel_ulong_t)&tasdevice_id[TAS2570] }, + { "TXNW2572", (kernel_ulong_t)&tasdevice_id[TAS2572] }, + { "TXNW2574", (kernel_ulong_t)&tasdevice_id[TAS2574] }, + { "TXNW2781", (kernel_ulong_t)&tasdevice_id[TAS2781] }, + { "TXNW5802", (kernel_ulong_t)&tasdevice_id[TAS5802] }, + { "TXNW806M", (kernel_ulong_t)&tasdevice_id[TAS5806M] }, + { "TXNW806D", (kernel_ulong_t)&tasdevice_id[TAS5806MD] }, + { "TXNW5815", (kernel_ulong_t)&tasdevice_id[TAS5815] }, + { "TXNW5822", (kernel_ulong_t)&tasdevice_id[TAS5822] }, + { "TXNW5825", (kernel_ulong_t)&tasdevice_id[TAS5825] }, + { "TXNW5827", (kernel_ulong_t)&tasdevice_id[TAS5827] }, + { "TXNW5828", (kernel_ulong_t)&tasdevice_id[TAS5828] }, + { "TXNW5830", (kernel_ulong_t)&tasdevice_id[TAS5830] }, {}, }; @@ -2121,7 +2120,6 @@ static struct i2c_driver tasdevice_i2c_driver = { }, .probe = tasdevice_i2c_probe, .remove = tasdevice_i2c_remove, - .id_table = tasdevice_id, }; module_i2c_driver(tasdevice_i2c_driver); diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 605da1259fc6..223c49dfc450 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -91,7 +91,6 @@ struct tlv320dac33_priv { int mode1_latency; /* latency caused by the i2c writes in * us */ u8 burst_bclkdiv; /* BCLK divider value in burst mode */ - u8 *reg_cache; unsigned int burst_rate; /* Interface speed in Burst modes */ int keep_bclk; /* Keep the BCLK continuously running @@ -108,6 +107,8 @@ struct tlv320dac33_priv { enum dac33_state state; struct i2c_client *i2c; + + u8 reg_cache[]; }; static const u8 dac33_reg[DAC33_CACHEREGNUM] = { @@ -1477,15 +1478,12 @@ static int dac33_i2c_probe(struct i2c_client *client) struct tlv320dac33_priv *dac33; int ret, i; - dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv), + dac33 = devm_kzalloc(&client->dev, struct_size(dac33, reg_cache, ARRAY_SIZE(dac33_reg)), GFP_KERNEL); if (dac33 == NULL) return -ENOMEM; - dac33->reg_cache = devm_kmemdup_array(&client->dev, dac33_reg, ARRAY_SIZE(dac33_reg), - sizeof(dac33_reg[0]), GFP_KERNEL); - if (!dac33->reg_cache) - return -ENOMEM; + memcpy(dac33->reg_cache, dac33_reg, ARRAY_SIZE(dac33_reg)); dac33->i2c = client; mutex_init(&dac33->mutex); diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 5ce0db9326fd..5a7beeadb009 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -5,7 +5,6 @@ * Copyright (C) 2014 Google, Inc. */ -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/input.h> diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 9e9c540a45ca..55c83d95bfba 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -16,16 +16,19 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/i2c.h> +#include <linux/property.h> #include <linux/workqueue.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> -#include <sound/uda1380.h> + +#define UDA1380_DAC_CLK_SYSCLK 0 +#define UDA1380_DAC_CLK_WSPLL 1 #include "uda1380.h" @@ -36,6 +39,8 @@ struct uda1380_priv { struct work_struct work; struct i2c_client *i2c; u16 *reg_cache; + struct gpio_desc *power; + struct gpio_desc *reset; }; /* @@ -95,6 +100,8 @@ static int uda1380_write(struct snd_soc_component *component, unsigned int reg, { struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); u8 data[3]; + unsigned int val; + int ret; /* data is * data[0] is register offset @@ -113,21 +120,38 @@ static int uda1380_write(struct snd_soc_component *component, unsigned int reg, if (!snd_soc_component_active(component) && (reg >= UDA1380_MVOL)) return 0; pr_debug("uda1380: hw write %x val %x\n", reg, value); - if (i2c_master_send(uda1380->i2c, data, 3) == 3) { - unsigned int val; - i2c_master_send(uda1380->i2c, data, 1); - i2c_master_recv(uda1380->i2c, data, 2); - val = (data[0]<<8) | data[1]; - if (val != value) { - pr_debug("uda1380: READ BACK VAL %x\n", - (data[0]<<8) | data[1]); - return -EIO; - } - if (reg >= 0x10) - clear_bit(reg - 0x10, &uda1380_cache_dirty); - return 0; - } else + + ret = i2c_master_send(uda1380->i2c, data, 3); + if (ret != 3) { + int err = ret < 0 ? ret : -EIO; + dev_err(component->dev, "write failed: %pe\n", ERR_PTR(err)); + return err; + } + + ret = i2c_master_send(uda1380->i2c, data, 1); + if (ret != 1) { + int err = ret < 0 ? ret : -EIO; + dev_err(component->dev, "send address failed: %pe\n", ERR_PTR(err)); + return err; +} + + ret = i2c_master_recv(uda1380->i2c, data, 2); + if (ret != 2) { + int err = ret < 0 ? ret : -EIO; + dev_err(component->dev, "read failed: %pe\n", ERR_PTR(err)); + return err; + } + + val = (data[0] << 8) | data[1]; + if (val != value) { + dev_err(component->dev, "read back val %x (expected %x)\n", val, value); return -EIO; + } + + if (reg >= 0x10) + clear_bit(reg - 0x10, &uda1380_cache_dirty); + + return 0; } static void uda1380_sync_cache(struct snd_soc_component *component) @@ -150,13 +174,12 @@ static void uda1380_sync_cache(struct snd_soc_component *component) static int uda1380_reset(struct snd_soc_component *component) { - struct uda1380_platform_data *pdata = component->dev->platform_data; struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(pdata->gpio_reset)) { - gpio_set_value(pdata->gpio_reset, 1); + if (uda1380->reset) { + gpiod_set_value(uda1380->reset, 1); mdelay(1); - gpio_set_value(pdata->gpio_reset, 0); + gpiod_set_value(uda1380->reset, 0); } else { u8 data[3]; @@ -589,9 +612,9 @@ static int uda1380_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); int pm = uda1380_read_reg_cache(component, UDA1380_PM); int reg; - struct uda1380_platform_data *pdata = component->dev->platform_data; switch (level) { case SND_SOC_BIAS_ON: @@ -601,8 +624,8 @@ static int uda1380_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_OFF) { - if (gpio_is_valid(pdata->gpio_power)) { - gpio_set_value(pdata->gpio_power, 1); + if (uda1380->power) { + gpiod_set_value(uda1380->power, 1); mdelay(1); uda1380_reset(component); } @@ -612,10 +635,10 @@ static int uda1380_set_bias_level(struct snd_soc_component *component, uda1380_write(component, UDA1380_PM, 0x0); break; case SND_SOC_BIAS_OFF: - if (!gpio_is_valid(pdata->gpio_power)) + if (!uda1380->power) break; - gpio_set_value(pdata->gpio_power, 0); + gpiod_set_value(uda1380->power, 0); /* Mark mixer regs cache dirty to sync them with * codec regs on power on. @@ -694,13 +717,12 @@ static struct snd_soc_dai_driver uda1380_dai[] = { static int uda1380_probe(struct snd_soc_component *component) { - struct uda1380_platform_data *pdata =component->dev->platform_data; struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); int ret; uda1380->component = component; - if (!gpio_is_valid(pdata->gpio_power)) { + if (!uda1380->power) { ret = uda1380_reset(component); if (ret) return ret; @@ -709,7 +731,7 @@ static int uda1380_probe(struct snd_soc_component *component) INIT_WORK(&uda1380->work, uda1380_flush_work); /* set clock input */ - switch (pdata->dac_clk) { + switch (uda1380->dac_clk) { case UDA1380_DAC_CLK_SYSCLK: uda1380_write_reg_cache(component, UDA1380_CLK, 0); break; @@ -741,31 +763,31 @@ static const struct snd_soc_component_driver soc_component_dev_uda1380 = { static int uda1380_i2c_probe(struct i2c_client *i2c) { - struct uda1380_platform_data *pdata = i2c->dev.platform_data; + struct device *dev = &i2c->dev; struct uda1380_priv *uda1380; int ret; - if (!pdata) - return -EINVAL; - uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv), GFP_KERNEL); if (uda1380 == NULL) return -ENOMEM; - if (gpio_is_valid(pdata->gpio_reset)) { - ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_reset, - GPIOF_OUT_INIT_LOW, "uda1380 reset"); - if (ret) - return ret; - } - - if (gpio_is_valid(pdata->gpio_power)) { - ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_power, - GPIOF_OUT_INIT_LOW, "uda1380 power"); - if (ret) - return ret; - } + uda1380->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(uda1380->reset)) + return dev_err_probe(dev, PTR_ERR(uda1380->reset), + "error obtaining reset GPIO\n"); + gpiod_set_consumer_name(uda1380->reset, "uda1380 reset"); + + uda1380->power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(uda1380->power)) + return dev_err_probe(dev, PTR_ERR(uda1380->power), + "error obtaining power GPIO\n"); + gpiod_set_consumer_name(uda1380->power, "uda1380 power"); + + /* This is just some default */ + uda1380->dac_clk = UDA1380_DAC_CLK_SYSCLK; + if (device_property_match_string(dev, "dac-clk", "wspll") >= 0) + uda1380->dac_clk = UDA1380_DAC_CLK_WSPLL; uda1380->reg_cache = devm_kmemdup_array(&i2c->dev, uda1380_reg, ARRAY_SIZE(uda1380_reg), sizeof(uda1380_reg[0]), GFP_KERNEL); diff --git a/sound/soc/codecs/wcd-clsh-v2.c b/sound/soc/codecs/wcd-clsh-v2.c index 13d07296916f..62ca22ea0f3b 100644 --- a/sound/soc/codecs/wcd-clsh-v2.c +++ b/sound/soc/codecs/wcd-clsh-v2.c @@ -848,9 +848,6 @@ int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, { struct snd_soc_component *comp = ctrl->comp; - if (nstate == ctrl->state) - return 0; - if (!wcd_clsh_is_state_valid(nstate)) { dev_err(comp->dev, "Class-H not a valid new state:\n"); return -EINVAL; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 640e43ee1975..e3ca5ca6de3d 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -3907,7 +3907,7 @@ static irqreturn_t wcd9335_slimbus_irq(int irq, void *data) { struct wcd9335_codec *wcd = data; unsigned long status = 0; - int i, j, port_id; + unsigned int i, j, port_id; unsigned int val, int_val = 0; irqreturn_t ret = IRQ_NONE; bool tx; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 2e23848e1dce..a637e22c3929 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -7,6 +7,8 @@ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> */ +#include <kunit/static_stub.h> +#include <kunit/visibility.h> #include <linux/array_size.h> #include <linux/cleanup.h> #include <linux/ctype.h> @@ -72,7 +74,7 @@ static const struct cs_dsp_client_ops wm_adsp2_client_ops; #define WM_ADSP_NUM_FW 13 -static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { +static const char * const wm_adsp_fw_text[WM_ADSP_NUM_FW] = { [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", [WM_ADSP_FW_HIFI] = "MasterHiFi", [WM_ADSP_FW_TX] = "Tx", @@ -316,6 +318,17 @@ struct wm_coeff_ctl { struct work_struct work; }; +#if IS_ENABLED(CONFIG_KUNIT) +const char *wm_adsp_get_fwf_name_by_index(int index) +{ + if (index < ARRAY_SIZE(wm_adsp_fw)) + return wm_adsp_fw[index].file; + + return NULL; +} +EXPORT_SYMBOL_IF_KUNIT(wm_adsp_get_fwf_name_by_index); +#endif + int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -704,21 +717,30 @@ int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, } EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); -static void wm_adsp_release_firmware_files(struct wm_adsp *dsp, - const struct firmware *wmfw_firmware, - char *wmfw_filename, - const struct firmware *coeff_firmware, - char *coeff_filename) +VISIBLE_IF_KUNIT void wm_adsp_release_firmware_files(struct wm_adsp_fw_files *fw) +{ + KUNIT_STATIC_STUB_REDIRECT(wm_adsp_release_firmware_files, fw); + + release_firmware(fw->wmfw.firmware); + kfree(fw->wmfw.filename); + + release_firmware(fw->coeff.firmware); + kfree(fw->coeff.filename); +} +EXPORT_SYMBOL_IF_KUNIT(wm_adsp_release_firmware_files); + +VISIBLE_IF_KUNIT int wm_adsp_firmware_request(const struct firmware **firmware, + const char *filename, + struct device *dev) { - release_firmware(wmfw_firmware); - kfree(wmfw_filename); + KUNIT_STATIC_STUB_REDIRECT(wm_adsp_firmware_request, firmware, filename, dev); - release_firmware(coeff_firmware); - kfree(coeff_filename); + return firmware_request_nowarn(firmware, filename, dev); } +EXPORT_SYMBOL_IF_KUNIT(wm_adsp_firmware_request); static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, - const struct firmware **firmware, char **filename, + struct wm_adsp_fw_file *fw, const char *dir, const char *system_name, const char *asoc_component_prefix, const char *filetype) @@ -726,7 +748,7 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, struct cs_dsp *cs_dsp = &dsp->cs_dsp; const char *fwf; char *s, c; - int ret = 0; + int ret; if (dsp->fwf_name) fwf = dsp->fwf_name; @@ -734,119 +756,128 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, fwf = dsp->cs_dsp.name; if (system_name && asoc_component_prefix) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part, - fwf, wm_adsp_fw[dsp->fw].file, system_name, - asoc_component_prefix, filetype); + fw->filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part, + fwf, wm_adsp_fw[dsp->fw].file, system_name, + asoc_component_prefix, filetype); else if (system_name) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part, - fwf, wm_adsp_fw[dsp->fw].file, system_name, - filetype); + fw->filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part, + fwf, wm_adsp_fw[dsp->fw].file, system_name, + filetype); else - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, fwf, - wm_adsp_fw[dsp->fw].file, filetype); + fw->filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, fwf, + wm_adsp_fw[dsp->fw].file, filetype); - if (*filename == NULL) + if (!fw->filename) return -ENOMEM; /* - * Make sure that filename is lower-case and any non alpha-numeric - * characters except full stop and forward slash are replaced with - * hyphens. + * Make sure that filename after dir is lower-case and any non-alpha-numeric + * characters except full-stop are replaced with hyphens. */ - s = *filename; + s = fw->filename + strlen(dir); while (*s) { c = *s; if (isalnum(c)) *s = tolower(c); - else if ((c != '.') && (c != '/')) + else if (c != '.') *s = '-'; s++; } - ret = firmware_request_nowarn(firmware, *filename, cs_dsp->dev); - if (ret != 0) { - adsp_dbg(dsp, "Failed to request '%s'\n", *filename); - kfree(*filename); - *filename = NULL; + ret = wm_adsp_firmware_request(&fw->firmware, fw->filename, cs_dsp->dev); + if (ret < 0) { + adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret); + kfree(fw->filename); + fw->filename = NULL; + if (ret != -ENOENT) + return ret; } else { - adsp_dbg(dsp, "Found '%s'\n", *filename); + adsp_dbg(dsp, "Found '%s'\n", fw->filename); } - return ret; + return 0; } static const char * const cirrus_dir = "cirrus/"; -static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, - const struct firmware **wmfw_firmware, - char **wmfw_filename, - const struct firmware **coeff_firmware, - char **coeff_filename) +VISIBLE_IF_KUNIT int wm_adsp_request_firmware_files(struct wm_adsp *dsp, + struct wm_adsp_fw_files *fw) { const char *system_name = dsp->system_name; const char *suffix = dsp->component->name_prefix; + bool require_bin_suffix = false; int ret = 0; if (dsp->fwf_suffix) suffix = dsp->fwf_suffix; - if (system_name && suffix) { - if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, - cirrus_dir, system_name, - suffix, "wmfw")) { - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - cirrus_dir, system_name, - suffix, "bin"); - return 0; - } - } - if (system_name) { - if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, - cirrus_dir, system_name, - NULL, "wmfw")) { - if (suffix) - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - cirrus_dir, system_name, - suffix, "bin"); - - if (!*coeff_firmware) - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - cirrus_dir, system_name, - NULL, "bin"); - return 0; - } - } + ret = wm_adsp_request_firmware_file(dsp, &fw->wmfw, + cirrus_dir, system_name, + suffix, "wmfw"); + if (ret < 0) + goto err; - /* Check system-specific bin without wmfw before falling back to generic */ - if (dsp->wmfw_optional && system_name) { - if (suffix) - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - cirrus_dir, system_name, - suffix, "bin"); + if (suffix) { + if (fw->wmfw.firmware) { + require_bin_suffix = true; + } else { + /* Fallback to name without suffix */ + ret = wm_adsp_request_firmware_file(dsp, &fw->wmfw, + cirrus_dir, system_name, + NULL, "wmfw"); + if (ret < 0) + goto err; + } + } - if (!*coeff_firmware) - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - cirrus_dir, system_name, - NULL, "bin"); + /* Look for matching .bin file */ + if (fw->wmfw.firmware || dsp->wmfw_optional) { + ret = wm_adsp_request_firmware_file(dsp, &fw->coeff, + cirrus_dir, system_name, + suffix, "bin"); + if (ret < 0) + goto err; + + if (suffix && !fw->coeff.firmware && !require_bin_suffix) { + /* Fallback to name without suffix */ + ret = wm_adsp_request_firmware_file(dsp, + &fw->coeff, + cirrus_dir, system_name, + NULL, "bin"); + if (ret < 0) + goto err; + } + } - if (*coeff_firmware) + if (fw->wmfw.firmware || (dsp->wmfw_optional && fw->coeff.firmware)) return 0; } /* Check legacy location */ - if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, - "", NULL, NULL, "wmfw")) { - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - "", NULL, NULL, "bin"); + ret = wm_adsp_request_firmware_file(dsp, &fw->wmfw, "", NULL, NULL, "wmfw"); + if (ret < 0) + goto err; + + if (fw->wmfw.firmware) { + ret = wm_adsp_request_firmware_file(dsp, &fw->coeff, "", NULL, NULL, "bin"); + if (ret < 0) + goto err; + return 0; } /* Fall back to generic wmfw and optional matching bin */ - ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, + ret = wm_adsp_request_firmware_file(dsp, &fw->wmfw, cirrus_dir, NULL, NULL, "wmfw"); - if (!ret || dsp->wmfw_optional) { - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - cirrus_dir, NULL, NULL, "bin"); + if (ret < 0) + goto err; + + if (fw->wmfw.firmware || dsp->wmfw_optional) { + ret = wm_adsp_request_firmware_file(dsp, &fw->coeff, + cirrus_dir, NULL, NULL, "bin"); + if (ret < 0) + goto err; + return 0; } @@ -855,8 +886,13 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name, wm_adsp_fw[dsp->fw].file, system_name, suffix); - return -ENOENT; + ret = -ENOENT; +err: + wm_adsp_release_firmware_files(fw); + + return ret; } +EXPORT_SYMBOL_IF_KUNIT(wm_adsp_request_firmware_files); static int wm_adsp_common_init(struct wm_adsp *dsp) { @@ -887,30 +923,23 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct wm_adsp *dsp = &dsps[w->shift]; + struct wm_adsp_fw_files fw = { 0 }; int ret = 0; - char *wmfw_filename = NULL; - const struct firmware *wmfw_firmware = NULL; - char *coeff_filename = NULL; - const struct firmware *coeff_firmware = NULL; dsp->component = component; switch (event) { case SND_SOC_DAPM_POST_PMU: - ret = wm_adsp_request_firmware_files(dsp, - &wmfw_firmware, &wmfw_filename, - &coeff_firmware, &coeff_filename); + ret = wm_adsp_request_firmware_files(dsp, &fw); if (ret) break; ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp, - wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename, + fw.wmfw.firmware, fw.wmfw.filename, + fw.coeff.firmware, fw.coeff.filename, wm_adsp_fw_text[dsp->fw]); - wm_adsp_release_firmware_files(dsp, - wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename); + wm_adsp_release_firmware_files(&fw); break; case SND_SOC_DAPM_PRE_PMD: cs_dsp_adsp1_power_down(&dsp->cs_dsp); @@ -986,34 +1015,27 @@ EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware) { + struct wm_adsp_fw_files fw = { 0 }; int ret = 0; - char *wmfw_filename = NULL; - const struct firmware *wmfw_firmware = NULL; - char *coeff_filename = NULL; - const struct firmware *coeff_firmware = NULL; if (load_firmware) { - ret = wm_adsp_request_firmware_files(dsp, - &wmfw_firmware, &wmfw_filename, - &coeff_firmware, &coeff_filename); + ret = wm_adsp_request_firmware_files(dsp, &fw); if (ret) return ret; } - if (dsp->bin_mandatory && !coeff_firmware) { + if (dsp->bin_mandatory && !fw.coeff.firmware) { ret = -ENOENT; goto err; } ret = cs_dsp_power_up(&dsp->cs_dsp, - wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename, + fw.wmfw.firmware, fw.wmfw.filename, + fw.coeff.firmware, fw.coeff.filename, wm_adsp_fw_text[dsp->fw]); err: - wm_adsp_release_firmware_files(dsp, - wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename); + wm_adsp_release_firmware_files(&fw); return ret; } @@ -1100,6 +1122,12 @@ void wm_adsp_stop(struct wm_adsp *dsp) } EXPORT_SYMBOL_GPL(wm_adsp_stop); +void wm_adsp_hibernate(struct wm_adsp *dsp, bool hibernate) +{ + cs_dsp_hibernate(&dsp->cs_dsp, hibernate); +} +EXPORT_SYMBOL_GPL(wm_adsp_hibernate); + int wm_adsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 8035fda71f8d..8922732479c2 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -79,6 +79,16 @@ struct wm_adsp { SOC_ENUM_EXT(dspname " Firmware", wm_adsp_fw_enum[num], \ wm_adsp_fw_get, wm_adsp_fw_put) +struct wm_adsp_fw_file { + const struct firmware *firmware; + char *filename; +}; + +struct wm_adsp_fw_files { + struct wm_adsp_fw_file wmfw; + struct wm_adsp_fw_file coeff; +}; + extern const struct soc_enum wm_adsp_fw_enum[]; int wm_adsp1_init(struct wm_adsp *dsp); @@ -103,6 +113,7 @@ irqreturn_t wm_halo_wdt_expire(int irq, void *data); int wm_adsp_run(struct wm_adsp *dsp); void wm_adsp_stop(struct wm_adsp *dsp); +void wm_adsp_hibernate(struct wm_adsp *dsp, bool hibernate); int wm_adsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -142,4 +153,13 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, unsigned int alg, void *buf, size_t len); +#if IS_ENABLED(CONFIG_KUNIT) +const char *wm_adsp_get_fwf_name_by_index(int index); +void wm_adsp_release_firmware_files(struct wm_adsp_fw_files *fw); +int wm_adsp_firmware_request(const struct firmware **firmware, + const char *filename, + struct device *dev); +int wm_adsp_request_firmware_files(struct wm_adsp *dsp, struct wm_adsp_fw_files *fw); +#endif + #endif diff --git a/sound/soc/codecs/wm_adsp_fw_find_test.c b/sound/soc/codecs/wm_adsp_fw_find_test.c new file mode 100644 index 000000000000..d0c7fb30a95d --- /dev/null +++ b/sound/soc/codecs/wm_adsp_fw_find_test.c @@ -0,0 +1,1223 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Test cases for wm_adsp library. +// +// Copyright (C) 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <kunit/device.h> +#include <kunit/static_stub.h> +#include <kunit/test.h> +#include <linux/slab.h> +#include "wm_adsp.h" + +KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *); + +struct wm_adsp_fw_find_test { + struct wm_adsp dsp; + + struct wm_adsp_fw_files found_fw; + char searched_fw_files[768]; +}; + +struct wm_adsp_fw_find_test_params { + const char *part; + const char *dsp_name; + const char *fwf_name; + const char *system_name; + const char *alsa_name; + bool wmfw_optional; + bool bin_mandatory; + + /* If non-NULL this file should be returned as "found" */ + const char *expect_wmfw; + + /* If non-NULL this file should be returned as "found" */ + const char *expect_bin; + + /* Space-separated list of filenames in expected order of searching */ + const char *expected_searches; + + /* NULL-terminated array of pointers to filenames to simulate directory content */ + const char * const *dir_files; +}; + +/* Dummy struct firmware to return from wm_adsp_request_firmware_files */ +static const struct firmware wm_adsp_find_test_dummy_firmware; + +/* Simple lookup of a filename in a list of names */ +static int wm_adsp_fw_find_test_firmware_request_simple_stub(const struct firmware **firmware, + const char *filename, + struct device *dev) +{ + struct kunit *test = kunit_get_current_test(); + const struct wm_adsp_fw_find_test_params *params = test->param_value; + int i; + + /* Non-parameterized test? */ + if (!params) + return -ENOENT; + + if (!params->dir_files) + return -ENOENT; + + for (i = 0; params->dir_files[i]; i++) { + if (strcmp(params->dir_files[i], filename) == 0) { + *firmware = &wm_adsp_find_test_dummy_firmware; + return 0; + } + } + + return -ENOENT; +} + +static void wm_adsp_fw_find_test_pick_file(struct kunit *test) +{ + struct wm_adsp_fw_find_test *priv = test->priv; + const struct wm_adsp_fw_find_test_params *params = test->param_value; + struct wm_adsp *dsp = &priv->dsp; + int i, ret; + + /* Concatenate string of dir content for error messages */ + for (i = 0; params->dir_files[i]; i++) { + strlcat(priv->searched_fw_files, params->dir_files[i], + sizeof(priv->searched_fw_files)); + strlcat(priv->searched_fw_files, ";", + sizeof(priv->searched_fw_files)); + } + + dsp->cs_dsp.name = params->dsp_name; + dsp->part = params->part; + dsp->fwf_name = params->fwf_name; + dsp->system_name = params->system_name; + dsp->component->name_prefix = params->alsa_name; + dsp->wmfw_optional = params->wmfw_optional; + dsp->bin_mandatory = params->bin_mandatory; + + kunit_activate_static_stub(test, + wm_adsp_firmware_request, + wm_adsp_fw_find_test_firmware_request_simple_stub); + + ret = wm_adsp_request_firmware_files(dsp, &priv->found_fw); + kunit_deactivate_static_stub(test, wm_adsp_firmware_request); + KUNIT_EXPECT_EQ_MSG(test, ret, + (params->expect_wmfw || params->expect_bin) ? 0 : -ENOENT, + "%s\n", priv->searched_fw_files); + + KUNIT_EXPECT_EQ_MSG(test, !!priv->found_fw.wmfw.filename, !!params->expect_wmfw, + "%s\n", priv->searched_fw_files); + KUNIT_EXPECT_EQ_MSG(test, !!priv->found_fw.coeff.filename, !!params->expect_bin, + "%s\n", priv->searched_fw_files); + + if (params->expect_wmfw) { + KUNIT_EXPECT_STREQ_MSG(test, priv->found_fw.wmfw.filename, params->expect_wmfw, + "%s\n", priv->searched_fw_files); + } + + if (params->expect_bin) { + KUNIT_EXPECT_STREQ_MSG(test, priv->found_fw.coeff.filename, params->expect_bin, + "%s\n", priv->searched_fw_files); + } +} + +static int wm_adsp_fw_find_test_firmware_request_stub(const struct firmware **firmware, + const char *filename, + struct device *dev) +{ + struct kunit *test = kunit_get_current_test(); + const struct wm_adsp_fw_find_test_params *params = test->param_value; + struct wm_adsp_fw_find_test *priv = test->priv; + + /* + * Searches are accumulated as a single string of space-separated names. + * The list of expected searches are stored the same way in + * struct wm_adsp_fw_find_test_params. This allows for comparision using + * a simple KUNIT_EXPECT_STREQ(), which avoids the risk of bugs in a + * more complex custom comparison. + */ + if (priv->searched_fw_files[0] != '\0') + strlcat(priv->searched_fw_files, " ", sizeof(priv->searched_fw_files)); + + strlcat(priv->searched_fw_files, filename, sizeof(priv->searched_fw_files)); + + /* Non-parameterized test? */ + if (!params) + return -ENOENT; + + if (params->expect_wmfw && (strcmp(filename, params->expect_wmfw) == 0)) { + *firmware = &wm_adsp_find_test_dummy_firmware; + return 0; + } + + if (params->expect_bin && (strcmp(filename, params->expect_bin) == 0)) { + *firmware = &wm_adsp_find_test_dummy_firmware; + return 0; + } + + return -ENOENT; +} + +static void wm_adsp_fw_find_test_search_order(struct kunit *test) +{ + struct wm_adsp_fw_find_test *priv = test->priv; + const struct wm_adsp_fw_find_test_params *params = test->param_value; + struct wm_adsp *dsp = &priv->dsp; + + dsp->cs_dsp.name = params->dsp_name; + dsp->part = params->part; + dsp->fwf_name = params->fwf_name; + dsp->system_name = params->system_name; + dsp->component->name_prefix = params->alsa_name; + dsp->wmfw_optional = params->wmfw_optional; + + kunit_activate_static_stub(test, + wm_adsp_firmware_request, + wm_adsp_fw_find_test_firmware_request_stub); + + wm_adsp_request_firmware_files(dsp, &priv->found_fw); + + kunit_deactivate_static_stub(test, wm_adsp_firmware_request); + + KUNIT_EXPECT_STREQ(test, priv->searched_fw_files, params->expected_searches); + + KUNIT_EXPECT_EQ(test, !!priv->found_fw.wmfw.filename, !!params->expect_wmfw); + if (params->expect_wmfw) + KUNIT_EXPECT_STREQ(test, priv->found_fw.wmfw.filename, params->expect_wmfw); + + KUNIT_EXPECT_EQ(test, !!priv->found_fw.coeff.filename, !!params->expect_bin); + if (params->expect_bin) + KUNIT_EXPECT_STREQ(test, priv->found_fw.coeff.filename, params->expect_bin); + + /* Either we get a filename and firmware, or neither */ + KUNIT_EXPECT_EQ(test, !!priv->found_fw.wmfw.filename, !!priv->found_fw.wmfw.firmware); + KUNIT_EXPECT_EQ(test, !!priv->found_fw.coeff.filename, !!priv->found_fw.coeff.firmware); +} + +static void wm_adsp_fw_find_test_find_firmware_byindex(struct kunit *test) +{ + struct wm_adsp_fw_find_test *priv = test->priv; + struct wm_adsp *dsp = &priv->dsp; + const char *fw_name; + + dsp->cs_dsp.name = "cs1234"; + dsp->part = "dsp1"; + for (dsp->fw = 0;; dsp->fw++) { + fw_name = wm_adsp_get_fwf_name_by_index(dsp->fw); + if (!fw_name) + break; + + kunit_activate_static_stub(test, + wm_adsp_firmware_request, + wm_adsp_fw_find_test_firmware_request_stub); + + wm_adsp_request_firmware_files(dsp, &priv->found_fw); + kunit_deactivate_static_stub(test, wm_adsp_firmware_request); + + KUNIT_EXPECT_NOT_NULL_MSG(test, + strstr(priv->searched_fw_files, fw_name), + "fw#%d Did not find '%s' in '%s'\n", + dsp->fw, fw_name, priv->searched_fw_files); + } +} + +static int wm_adsp_fw_find_test_case_init(struct kunit *test) +{ + struct wm_adsp_fw_find_test *priv; + struct device *test_dev; + int ret; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Require dummy struct snd_soc_component for the alsa name prefix string */ + priv->dsp.component = kunit_kzalloc(test, sizeof(*priv->dsp.component), GFP_KERNEL); + if (!priv->dsp.component) + return -ENOMEM; + + test->priv = priv; + + /* Create dummy amp device */ + test_dev = kunit_device_register(test, "wm_adsp_test_drv"); + if (IS_ERR(test_dev)) + return PTR_ERR(test_dev); + + priv->dsp.cs_dsp.dev = get_device(test_dev); + if (!priv->dsp.cs_dsp.dev) + return -ENODEV; + + ret = kunit_add_action_or_reset(test, _put_device_wrapper, priv->dsp.cs_dsp.dev); + if (ret) + return ret; + + return 0; +} + +static void wm_adsp_fw_find_test_case_exit(struct kunit *test) +{ + struct wm_adsp_fw_find_test *priv = test->priv; + + /* + * priv->found_wmfw_firmware and priv->found_bin_firmware are + * dummies not allocated by the real request_firmware() call they + * must not be passed to release_firmware(). + */ + kfree(priv->found_fw.wmfw.filename); + kfree(priv->found_fw.coeff.filename); +} + +static void wm_adsp_fw_find_test_param_desc(const struct wm_adsp_fw_find_test_params *param, + char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, + "%s %s fwf_name:%s system:%s alsa_name:%s %s expects:(%s %s)", + param->part, param->dsp_name, + param->fwf_name ? param->fwf_name : "", + param->system_name ? param->system_name : "", + param->alsa_name ? param->alsa_name : "", + param->wmfw_optional ? "wmfw_optional" : "", + param->expect_wmfw ? param->expect_wmfw : "", + param->expect_bin ? param->expect_bin : ""); +} + +/* Cases where firmware file not found. Tests full search sequence. */ +static const struct wm_adsp_fw_find_test_params wm_adsp_fw_find_full_search_cases[] = { + { /* system name and alsa prefix, wmfw mandatory. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw", + }, + { /* system name and alsa prefix, wmfw optional. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* system name only, wmfw mandatory. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw", + }, + { /* system name only, wmfw optional. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .wmfw_optional = true, + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + + /* + * TODO: Is this a bug? Device-specific bin is only allowed when there + * is a system_name. But if there isn't any meaningful system name on + * a product, why can't it load firmware files qualified by alsa prefix? + */ + + { /* Alsa prefix, wmfw mandatory. No system name so generic files only. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw", + }, + { /* Alsa prefix, wmfw optional. No system name so generic files only. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .wmfw_optional = true, + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + + { /* fwf_name, system name and alsa prefix, wmfw mandatory. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .fwf_name = "ao", + .expected_searches = + "cirrus/cs1234-ao-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-ao-mbc-vss-abc123.wmfw " + "cs1234-ao-mbc-vss.wmfw " + "cirrus/cs1234-ao-mbc-vss.wmfw", + }, + { /* fwf_name, system name and alsa prefix, wmfw optional. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .fwf_name = "ao", + .wmfw_optional = true, + .expected_searches = + "cirrus/cs1234-ao-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-ao-mbc-vss-abc123.wmfw " + "cirrus/cs1234-ao-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-ao-mbc-vss-abc123.bin " + "cs1234-ao-mbc-vss.wmfw " + "cirrus/cs1234-ao-mbc-vss.wmfw " + "cirrus/cs1234-ao-mbc-vss.bin", + }, +}; +KUNIT_ARRAY_PARAM(wm_adsp_fw_find_full_search, + wm_adsp_fw_find_full_search_cases, + wm_adsp_fw_find_test_param_desc); + +/* Cases with system name and alsa prefix both given. */ +static const struct wm_adsp_fw_find_test_params wm_adsp_fw_find_system_alsaname_cases[] = { + { /* Fully-qualified wmfw exists. No bin */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + }, + { /* Optional fully-qualified wmfw exists. No bin */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + }, + { /* Fully-qualified wmfw and bin exist. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + }, + { /* Optional fully-qualified wmfw and fully-qualified bin exist. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + }, + { /* wmfw matches system name only. No bin */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* Optional wmfw matches system name only. No bin */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* wmfw matches system name only. Fully-qualified bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + }, + { /* Optional wmfw matches system name only. Fully-qualified bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + }, + { /* wmfw and bin match system name only. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* Optional wmfw and bin match system name only. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* Optional wmfw not found. bin matches fully-qualified name. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin", + }, + { /* Optional wmfw not found. bin matches system name only. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* No qualified wmfw. Legacy generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified optional wmfw. Legacy generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified wmfw. Legacy generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified optional wmfw. Legacy generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified or legacy wmfw. Generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No optional qualified or legacy wmfw. Generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified or legacy wmfw. Generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No optional qualified or legacy wmfw. Generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No optional qualified or generic wmfw. Generic bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123-amp1.bin " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, +}; +KUNIT_ARRAY_PARAM(wm_adsp_fw_find_system_alsaname, + wm_adsp_fw_find_system_alsaname_cases, + wm_adsp_fw_find_test_param_desc); + +/* Cases with system name but without alsa name prefix. */ +static const struct wm_adsp_fw_find_test_params wm_adsp_fw_find_system_cases[] = { + { /* Qualified wmfw found. No bin */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* Optional qualified wmfw found. No bin */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* Qualified wmfw found. Qualified bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* Optional qualified wmfw found. Qualified bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* Optional wmfw not found. Qualified bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .wmfw_optional = true, + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin", + }, + { /* No qualified wmfw. Legacy generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified optional wmfw. Legacy generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .wmfw_optional = true, + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified wmfw. Legacy generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified optional wmfw. Legacy generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .wmfw_optional = true, + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified or legacy wmfw. Generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No optional qualified or legacy wmfw. Generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No qualified or legacy wmfw. Generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No optional qualified or legacy wmfw. Generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No optional qualified or generic wmfw. Generic bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "ABC123", + .wmfw_optional = true, + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc123.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc123.bin " + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, +}; +KUNIT_ARRAY_PARAM(wm_adsp_fw_find_system, + wm_adsp_fw_find_system_cases, + wm_adsp_fw_find_test_param_desc); + +/* Cases without system name but with alsa name prefix. */ +static const struct wm_adsp_fw_find_test_params wm_adsp_fw_find_alsaname_cases[] = { + { /* Legacy generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* wmfw optional. Legacy generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .wmfw_optional = true, + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* Legacy generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* wmfw optional. Legacy generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .wmfw_optional = true, + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy wmfw. Generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy wmfw. Optional generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy wmfw. Generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy wmfw. Optional generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy or generic wmfw. Generic bin found. */ + .part = "cs1234", .dsp_name = "dsp1", .alsa_name = "amp1", + .wmfw_optional = true, + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, +}; +KUNIT_ARRAY_PARAM(wm_adsp_fw_find_alsaname, + wm_adsp_fw_find_alsaname_cases, + wm_adsp_fw_find_test_param_desc); + +/* Cases without system name or alsa name prefix. */ +static const struct wm_adsp_fw_find_test_params wm_adsp_fw_find_noqual_cases[] = { + { /* Legacy generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* wmfw optional. Legacy generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", + .wmfw_optional = true, + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* Legacy generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* wmfw optional. Legacy generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", + .wmfw_optional = true, + .expect_wmfw = "cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy wmfw. Generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy wmfw. Optional generic wmfw found. No bin. */ + .part = "cs1234", .dsp_name = "dsp1", + .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy wmfw. Generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy wmfw. Optional generic wmfw and bin found. */ + .part = "cs1234", .dsp_name = "dsp1", + .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, + { /* No legacy or generic wmfw. Generic bin found. */ + .part = "cs1234", .dsp_name = "dsp1", + .wmfw_optional = true, + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .expected_searches = + "cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.wmfw " + "cirrus/cs1234-dsp1-mbc-vss.bin", + }, +}; +KUNIT_ARRAY_PARAM(wm_adsp_fw_find_noqual, + wm_adsp_fw_find_noqual_cases, + wm_adsp_fw_find_test_param_desc); + +/* + * Tests for filename normalization. The system name and alsa prefix strings + * should be converted to lower-case and delimiters are converted to '-', except + * for '.' which is preserved. + */ +static const struct wm_adsp_fw_find_test_params wm_adsp_fw_find_normalization_cases[] = { + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "Vendor", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-vendor.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-vendor.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-vendor.bin", + }, + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "Vendor Device", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-vendor-device.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-vendor-device.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-vendor-device.bin", + }, + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "Vendor_Device", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-vendor-device.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-vendor-device.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-vendor-device.bin", + }, + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "Vendor/Device", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-vendor-device.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-vendor-device.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-vendor-device.bin", + }, + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "1234:56AB", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-1234-56ab.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-1234-56ab.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-1234-56ab.bin", + }, + + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "abc", + .alsa_name = "LEFT", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc-left.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc-left.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc-left.bin", + }, + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "abc", + .alsa_name = "LEFT AMP", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc-left-amp.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc-left-amp.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc-left-amp.bin", + }, + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "abc", + .alsa_name = "Left Amp", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc-left-amp.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc-left-amp.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc-left-amp.bin", + }, + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "abc", + .alsa_name = "Amp_1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc-amp-1.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc-amp-1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc-amp-1.bin", + }, + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "abc", + .alsa_name = "cs1234.1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc-cs1234.1.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc-cs1234.1.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc-cs1234.1.bin", + }, + { + .part = "cs1234", .dsp_name = "dsp1", .system_name = "abc", + .alsa_name = "Spk/Jack", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-abc-spk-jack.wmfw", + .expected_searches = + "cirrus/cs1234-dsp1-mbc-vss-abc-spk-jack.wmfw " + "cirrus/cs1234-dsp1-mbc-vss-abc-spk-jack.bin", + }, +}; +KUNIT_ARRAY_PARAM(wm_adsp_fw_find_normalization, + wm_adsp_fw_find_normalization_cases, + wm_adsp_fw_find_test_param_desc); + +/* + * Dummy directory content for regression tests. + * DSP part name and system name are used to select different available + * files. + * + * System: + * WFBF1111 = wmfw and bin fully-qualified + * WSBF1111 = wmfw system-qualified, bin fully-qualified + * WSBS1111 = wmfw and bin system-qualified + * WFXX1111 = wmfw fully-qualified, bin not present + * XXBF1111 = wmfw not present, bin fully-qualified + * + * Part: + * cs1234 = for testing fully-qualified configurations + * cs1234nobin = generic wmfw without a bin available + * wm1234 = legacy wmfw and bin + * wm1234nobin = legacy wmfw without bin + */ +static const char * const wm_adsp_fw_find_test_dir_all_files[] = { + "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-amp1.wmfw", + "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-l1u2.wmfw", + "cirrus/cs1234-dsp1-mbc-vss-wfbf1111.wmfw", + "cirrus/cs1234-dsp1-mbc-vss-wsbf1111.wmfw", + "cirrus/cs1234-dsp1-mbc-vss-wsbs1111.wmfw", + "cirrus/cs1234-dsp1-mbc-vss-wfxx1111.wmfw", + "cirrus/cs1234-dsp1-mbc-vss.wmfw", + "cirrus/cs1234nobin-dsp1-mbc-vss.wmfw", + "cirrus/wm1234-dsp1-mbc-vss.wmfw", + "cirrus/wm1234nobin-dsp1-mbc-vss.wmfw", + "wm1234-dsp1-mbc-vss.wmfw", + "wm1234nobin-dsp1-mbc-vss.wmfw", + "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-amp1.bin", + "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-l1u2.bin", + "cirrus/cs1234-dsp1-mbc-vss-wsbf1111-amp1.bin", + "cirrus/cs1234-dsp1-mbc-vss-wsbf1111-l1u2.bin", + "cirrus/cs1234-dsp1-mbc-vss-wsbs1111.bin", + "cirrus/cs1234-dsp1-mbc-vss-xxbf1111-amp1.bin", + "cirrus/cs1234-dsp1-mbc-vss.bin", + "cirrus/wm1234-dsp1-mbc-vss.bin", + "wm1234-dsp1-mbc-vss.bin", + NULL /* terminator */ +}; + +/* + * Regression testing that a change in the search algorithm doesn't change + * which file is picked. This doesn't cover every possible combination, only + * those that are already in use and typical cases. + * + * It wouldn't be efficent to fully prove the algorithm this way (too many + * directory content combinations would be needed, and it only infers what the + * algorithm searched for, it doesn't prove exactly what searches were made). + * So the main testing is done by checking for the expected file searches. + * This regression test is independent of the search algorithm. + * + * The main tests already prove that the algorithm only searches for files + * with the correct qualifiers so we can assume that files with the wrong + * qualifiers would not be picked and there's no need to test for that here. + */ +static const struct wm_adsp_fw_find_test_params wm_adsp_fw_find_pick_cases[] = { + /* + * Amps + */ + { /* Full info, wmfw and bin fully-qualified */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WFBF1111", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-amp1.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-amp1.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw and bin fully-qualified */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WFBF1111", + .alsa_name = "l1u2", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-l1u2.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-l1u2.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw only system-qualified, bin fully-qualified */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WSBF1111", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wsbf1111.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-wsbf1111-amp1.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw only system-qualified, bin fully-qualified */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WSBF1111", + .alsa_name = "l1u2", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wsbf1111.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-wsbf1111-l1u2.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw optional but present, and bin fully-qualified */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WFBF1111", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-amp1.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-amp1.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw and bin only system-qualified */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WSBS1111", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wsbs1111.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-wsbs1111.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw optional but system-qualified wmfm present, bin fully-qualified */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WSBF1111", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wsbf1111.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-wsbf1111-amp1.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw optional not present, and bin fully-qualified */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "XXBF1111", + .alsa_name = "amp1", .wmfw_optional = true, + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-xxbf1111-amp1.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw and bin fully-qualified, bin mandatory and present */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WFBF1111", + .alsa_name = "amp1", .bin_mandatory = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-amp1.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss-wfbf1111-amp1.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw and bin fully-qualified, bin mandatory but not present */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WFXX1111", + .alsa_name = "amp1", .bin_mandatory = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wfxx1111.wmfw", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw optional but present, bin mandatory but not present */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "WFXX1111", + .alsa_name = "amp1", .wmfw_optional = true, .bin_mandatory = true, + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss-wfxx1111.wmfw", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw and bin not present, generic fallbacks are present */ + .part = "cs1234", .dsp_name = "dsp1", .system_name = "XXXX1111", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* Full info, wmfw and bin not present, generic wmfw present */ + .part = "cs1234nobin", .dsp_name = "dsp1", .system_name = "XXXX1111", + .alsa_name = "amp1", + .expect_wmfw = "cirrus/cs1234nobin-dsp1-mbc-vss.wmfw", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + + /* + * Codecs + */ + { /* No qualifiers. Generic wmfws exist, legacy should be chosen. */ + .part = "wm1234nobin", .dsp_name = "dsp1", + .expect_wmfw = "wm1234nobin-dsp1-mbc-vss.wmfw", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* No qualifiers. Generic wmfw and bin exist, legacy should be chosen */ + .part = "wm1234", .dsp_name = "dsp1", + .expect_wmfw = "wm1234-dsp1-mbc-vss.wmfw", + .expect_bin = "wm1234-dsp1-mbc-vss.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* No qualifiers. New generic wmfw exists, no legacy files. */ + .part = "cs1234nobin", .dsp_name = "dsp1", + .expect_wmfw = "cirrus/cs1234nobin-dsp1-mbc-vss.wmfw", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, + { /* No qualifiers. New generic wmfw and bin exist, no legacy files. */ + .part = "cs1234", .dsp_name = "dsp1", + .expect_wmfw = "cirrus/cs1234-dsp1-mbc-vss.wmfw", + .expect_bin = "cirrus/cs1234-dsp1-mbc-vss.bin", + .dir_files = wm_adsp_fw_find_test_dir_all_files, + }, +}; +KUNIT_ARRAY_PARAM(wm_adsp_fw_find_pick, + wm_adsp_fw_find_pick_cases, + wm_adsp_fw_find_test_param_desc); + +static struct kunit_case wm_adsp_fw_find_test_cases[] = { + KUNIT_CASE_PARAM(wm_adsp_fw_find_test_search_order, + wm_adsp_fw_find_full_search_gen_params), + + KUNIT_CASE_PARAM(wm_adsp_fw_find_test_search_order, + wm_adsp_fw_find_system_alsaname_gen_params), + + KUNIT_CASE_PARAM(wm_adsp_fw_find_test_search_order, + wm_adsp_fw_find_system_gen_params), + + KUNIT_CASE_PARAM(wm_adsp_fw_find_test_search_order, + wm_adsp_fw_find_alsaname_gen_params), + + KUNIT_CASE_PARAM(wm_adsp_fw_find_test_search_order, + wm_adsp_fw_find_noqual_gen_params), + + KUNIT_CASE_PARAM(wm_adsp_fw_find_test_search_order, + wm_adsp_fw_find_normalization_gen_params), + + KUNIT_CASE_PARAM(wm_adsp_fw_find_test_pick_file, + wm_adsp_fw_find_pick_gen_params), + + KUNIT_CASE(wm_adsp_fw_find_test_find_firmware_byindex), + + { } /* terminator */ +}; + +static struct kunit_suite wm_adsp_fw_find_test_suite = { + .name = "wm-adsp-fw-find", + .init = wm_adsp_fw_find_test_case_init, + .exit = wm_adsp_fw_find_test_case_exit, + .test_cases = wm_adsp_fw_find_test_cases, +}; + +kunit_test_suite(wm_adsp_fw_find_test_suite); + +MODULE_DESCRIPTION("KUnit test for Cirrus Logic wm_adsp driver"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 28001e9857d9..74dfd39fd604 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA SoC Synopsys I2S Audio Layer * @@ -5,10 +6,6 @@ * * Copyright (C) 2010 ST Microelectronics * Rajeev Kumar <rajeevkumar.linux@gmail.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> diff --git a/sound/soc/dwc/dwc-pcm.c b/sound/soc/dwc/dwc-pcm.c index a418265c030a..bbc4ea88edc4 100644 --- a/sound/soc/dwc/dwc-pcm.c +++ b/sound/soc/dwc/dwc-pcm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA SoC Synopsys PIO PCM for I2S driver * @@ -5,10 +6,6 @@ * * Copyright (C) 2016 Synopsys * Jose Abreu <joabreu@synopsys.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/io.h> @@ -256,7 +253,7 @@ static const struct snd_soc_component_driver dw_pcm_component = { .hw_params = dw_pcm_hw_params, .trigger = dw_pcm_trigger, .pointer = dw_pcm_pointer, - .pcm_construct = dw_pcm_new, + .pcm_new = dw_pcm_new, }; int dw_pcm_register(struct platform_device *pdev) diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h index dce88c9ad5f3..6510b3496371 100644 --- a/sound/soc/dwc/local.h +++ b/sound/soc/dwc/local.h @@ -1,9 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (ST) 2012 Rajeev Kumar (rajeevkumar.linux@gmail.com) - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __DESIGNWARE_LOCAL_H diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c index de17b103a4cf..4d3266d9cbb7 100644 --- a/sound/soc/fsl/efika-audio-fabric.c +++ b/sound/soc/fsl/efika-audio-fabric.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Efika driver for the PSC of the Freescale MPC52xx * configured as AC97 interface * * Copyright 2008 Jon Smirl, Digispeaker * Author: Jon Smirl <jonsmirl@gmail.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include <linux/init.h> diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 348b0aabfa68..5aa96af994c4 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -505,7 +505,7 @@ struct snd_soc_component_driver fsl_asrc_component = { .open = fsl_asrc_dma_startup, .close = fsl_asrc_dma_shutdown, .pointer = fsl_asrc_dma_pcm_pointer, - .pcm_construct = fsl_asrc_dma_pcm_new, + .pcm_new = fsl_asrc_dma_pcm_new, .legacy_dai_naming = 1, #ifdef CONFIG_DEBUG_FS .debugfs_prefix = "asrc", diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 26ddbe867b58..b12474880185 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -267,14 +267,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) /** * fsl_dma_new: initialize this PCM driver. * - * This function is called when the codec driver calls snd_soc_new_pcms(), - * once for each .dai_link in the machine driver's snd_soc_card - * structure. - * - * snd_dma_alloc_pages() is just a front-end to dma_alloc_coherent(), which - * (currently) always allocates the DMA buffer in lowmem, even if GFP_HIGHMEM - * is specified. Therefore, any DMA buffers we allocate will always be in low - * memory, but we support for 36-bit physical addresses anyway. + * This function is called by soc_new_pcm(), once for each DAI link + * in the machine driver's snd_soc_card structure. * * Regardless of where the memory is actually allocated, since the device can * technically DMA to any 36-bit address, we do need to set the DMA mask to 36. @@ -860,7 +854,7 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) 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_construct = fsl_dma_new; + dma->dai.pcm_new = fsl_dma_new; /* Store the SSI-specific information that we need */ dma->ssi_stx_phys = res.start + REG_SSI_STX0; diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 6c56134c60cc..114a6c0b6b73 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -54,6 +54,9 @@ static int fsl_easrc_iec958_put_bits(struct snd_kcontrol *kcontrol, unsigned int regval = ucontrol->value.integer.value[0]; int ret; + if (regval < EASRC_WIDTH_16_BIT || regval > EASRC_WIDTH_24_BIT) + return -EINVAL; + ret = (easrc_priv->bps_iec958[mc->regbase] != regval); easrc_priv->bps_iec958[mc->regbase] = regval; @@ -70,8 +73,16 @@ static int fsl_easrc_iec958_get_bits(struct snd_kcontrol *kcontrol, struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; - ucontrol->value.enumerated.item[0] = easrc_priv->bps_iec958[mc->regbase]; + ucontrol->value.integer.value[0] = easrc_priv->bps_iec958[mc->regbase]; + + return 0; +} +static int fsl_easrc_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; return 0; } @@ -81,11 +92,33 @@ static int fsl_easrc_get_reg(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; - unsigned int regval; + struct fsl_asrc *easrc = snd_soc_component_get_drvdata(component); + unsigned int *regval = (unsigned int *)ucontrol->value.iec958.status; + int ret; + + ret = regmap_read(easrc->regmap, REG_EASRC_CS0(mc->regbase), ®val[0]); + if (ret) + return ret; - regval = snd_soc_component_read(component, mc->regbase); + ret = regmap_read(easrc->regmap, REG_EASRC_CS1(mc->regbase), ®val[1]); + if (ret) + return ret; + + ret = regmap_read(easrc->regmap, REG_EASRC_CS2(mc->regbase), ®val[2]); + if (ret) + return ret; + + ret = regmap_read(easrc->regmap, REG_EASRC_CS3(mc->regbase), ®val[3]); + if (ret) + return ret; + + ret = regmap_read(easrc->regmap, REG_EASRC_CS4(mc->regbase), ®val[4]); + if (ret) + return ret; - ucontrol->value.integer.value[0] = regval; + ret = regmap_read(easrc->regmap, REG_EASRC_CS5(mc->regbase), ®val[5]); + if (ret) + return ret; return 0; } @@ -97,22 +130,62 @@ static int fsl_easrc_set_reg(struct snd_kcontrol *kcontrol, struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; struct fsl_asrc *easrc = snd_soc_component_get_drvdata(component); - unsigned int regval = ucontrol->value.integer.value[0]; - bool changed; + unsigned int *regval = (unsigned int *)ucontrol->value.iec958.status; + bool changed, changed_all = false; int ret; - ret = regmap_update_bits_check(easrc->regmap, mc->regbase, - GENMASK(31, 0), regval, &changed); - if (ret != 0) + ret = pm_runtime_resume_and_get(component->dev); + if (ret) return ret; - return changed; + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS0(mc->regbase), + GENMASK(31, 0), regval[0], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS1(mc->regbase), + GENMASK(31, 0), regval[1], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS2(mc->regbase), + GENMASK(31, 0), regval[2], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS3(mc->regbase), + GENMASK(31, 0), regval[3], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS4(mc->regbase), + GENMASK(31, 0), regval[4], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS5(mc->regbase), + GENMASK(31, 0), regval[5], &changed); + if (ret != 0) + goto err; + changed_all |= changed; +err: + pm_runtime_put_autosuspend(component->dev); + + if (ret != 0) + return ret; + else + return changed_all; } #define SOC_SINGLE_REG_RW(xname, xreg) \ { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .info = snd_soc_info_xr_sx, .get = fsl_easrc_get_reg, \ + .info = fsl_easrc_iec958_info, .get = fsl_easrc_get_reg, \ .put = fsl_easrc_set_reg, \ .private_value = (unsigned long)&(struct soc_mreg_control) \ { .regbase = xreg, .regcount = 1, .nbits = 32, \ @@ -143,30 +216,10 @@ static const struct snd_kcontrol_new fsl_easrc_snd_controls[] = { SOC_SINGLE_VAL_RW("Context 2 IEC958 Bits Per Sample", 2), SOC_SINGLE_VAL_RW("Context 3 IEC958 Bits Per Sample", 3), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS0", REG_EASRC_CS0(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS0", REG_EASRC_CS0(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS0", REG_EASRC_CS0(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS0", REG_EASRC_CS0(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS1", REG_EASRC_CS1(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS1", REG_EASRC_CS1(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS1", REG_EASRC_CS1(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS1", REG_EASRC_CS1(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS2", REG_EASRC_CS2(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS2", REG_EASRC_CS2(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS2", REG_EASRC_CS2(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS2", REG_EASRC_CS2(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS3", REG_EASRC_CS3(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS3", REG_EASRC_CS3(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS3", REG_EASRC_CS3(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS3", REG_EASRC_CS3(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS4", REG_EASRC_CS4(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS4", REG_EASRC_CS4(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS4", REG_EASRC_CS4(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS4", REG_EASRC_CS4(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS5", REG_EASRC_CS5(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS5", REG_EASRC_CS5(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS5", REG_EASRC_CS5(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS5", REG_EASRC_CS5(3)), + SOC_SINGLE_REG_RW("Context 0 IEC958 CS", 0), + SOC_SINGLE_REG_RW("Context 1 IEC958 CS", 1), + SOC_SINGLE_REG_RW("Context 2 IEC958 CS", 2), + SOC_SINGLE_REG_RW("Context 3 IEC958 CS", 3), }; /* @@ -1286,7 +1339,7 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) /* * Release the context * - * This funciton is mainly doing the revert thing in request context + * This function is mainly doing the revert thing in request context */ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) { diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index d6cde2757c6d..2e887f1f1f36 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -210,15 +210,23 @@ static int micfil_range_set(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; unsigned int shift = mc->shift; int max_range, new_range; + int ret; new_range = ucontrol->value.integer.value[0]; max_range = micfil_get_max_range(micfil); if (new_range > max_range) dev_warn(&micfil->pdev->dev, "range makes channel %d data unreliable\n", shift / 4); - regmap_update_bits(micfil->regmap, REG_MICFIL_OUT_CTRL, 0xF << shift, new_range << shift); + ret = pm_runtime_resume_and_get(cmpnt->dev); + if (ret) + return ret; - return 0; + ret = snd_soc_component_update_bits(cmpnt, REG_MICFIL_OUT_CTRL, 0xF << shift, + new_range << shift); + + pm_runtime_put_autosuspend(cmpnt->dev); + + return ret; } static int micfil_set_quality(struct fsl_micfil *micfil) @@ -281,10 +289,34 @@ static int micfil_quality_set(struct snd_kcontrol *kcontrol, { struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); + int val = ucontrol->value.integer.value[0]; + bool change = false; + int old_val; + int ret; + + if (val < QUALITY_HIGH || val > QUALITY_VLOW2) + return -EINVAL; + + if (micfil->quality != val) { + ret = pm_runtime_resume_and_get(cmpnt->dev); + if (ret) + return ret; + + old_val = micfil->quality; + micfil->quality = val; + ret = micfil_set_quality(micfil); - micfil->quality = ucontrol->value.integer.value[0]; + pm_runtime_put_autosuspend(cmpnt->dev); + + if (ret) { + micfil->quality = old_val; + return ret; + } - return micfil_set_quality(micfil); + change = true; + } + + return change; } static const char * const micfil_hwvad_enable[] = { @@ -343,6 +375,10 @@ static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, if (val < 0 || val > 3) return -EINVAL; + ret = pm_runtime_resume_and_get(comp->dev); + if (ret) + return ret; + micfil->dc_remover = val; /* Calculate total value for all channels */ @@ -352,10 +388,10 @@ static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, /* Update DC Remover mode for all channels */ ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL, MICFIL_DC_CTRL_CONFIG, reg_val); - if (ret < 0) - return ret; - return 0; + pm_runtime_put_autosuspend(comp->dev); + + return ret; } static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol, @@ -377,10 +413,15 @@ static int hwvad_put_enable(struct snd_kcontrol *kcontrol, unsigned int *item = ucontrol->value.enumerated.item; struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); int val = snd_soc_enum_item_to_val(e, item[0]); + bool change = false; + + if (val < 0 || val > 1) + return -EINVAL; + change = (micfil->vad_enabled != val); micfil->vad_enabled = val; - return 0; + return change; } static int hwvad_get_enable(struct snd_kcontrol *kcontrol, @@ -402,13 +443,18 @@ static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol, unsigned int *item = ucontrol->value.enumerated.item; struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); int val = snd_soc_enum_item_to_val(e, item[0]); + bool change = false; + + if (val < MICFIL_HWVAD_ENVELOPE_MODE || val > MICFIL_HWVAD_ENERGY_MODE) + return -EINVAL; /* 0 - Envelope-based Mode * 1 - Energy-based Mode */ + change = (micfil->vad_init_mode != val); micfil->vad_init_mode = val; - return 0; + return change; } static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol, @@ -503,7 +549,13 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0), SOC_SINGLE("HWVAD ZCD And Behavior Switch", REG_MICFIL_VAD0_ZCD, 4, 1, 0), - SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .name = "VAD Detected", + .info = snd_soc_info_bool_ext, + .get = hwvad_detected, + }, }; static int fsl_micfil_use_verid(struct device *dev) diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index 43d401ae2d03..76e014dfb6d7 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -54,8 +54,8 @@ struct qmc_dai_prtd { struct snd_pcm_substream *substream; }; -static int qmc_audio_pcm_construct(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) +static int qmc_audio_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; int ret; @@ -340,7 +340,7 @@ static const struct snd_soc_component_driver qmc_audio_soc_platform = { .hw_params = qmc_audio_pcm_hw_params, .trigger = qmc_audio_pcm_trigger, .pointer = qmc_audio_pcm_pointer, - .pcm_construct = qmc_audio_pcm_construct, + .pcm_new = qmc_audio_pcm_new, .of_xlate_dai_name = qmc_audio_of_xlate_dai_name, }; diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 148e09e58dfa..bd336d2e4cb3 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -41,6 +41,52 @@ static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { .list = fsl_sai_rates, }; +static const char * const inc_mode[] = { + "On enabled and bitcount increment", "On enabled" +}; + +static SOC_ENUM_SINGLE_DECL(transmit_tstmp_enum, + FSL_SAI_TTCTL, FSL_SAI_xTCTL_TSINC_SHIFT, inc_mode); +static SOC_ENUM_SINGLE_DECL(receive_tstmp_enum, + FSL_SAI_RTCTL, FSL_SAI_xTCTL_TSINC_SHIFT, inc_mode); + +static const struct snd_kcontrol_new fsl_sai_timestamp_ctrls[] = { + FSL_ASOC_SINGLE_EXT("Transmit Timestamp Control Switch", FSL_SAI_TTCTL, + FSL_SAI_xTCTL_TSEN_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_ENUM_EXT("Transmit Timestamp Increment", transmit_tstmp_enum, + fsl_asoc_get_enum_double, fsl_asoc_put_enum_double), + FSL_ASOC_SINGLE_EXT("Transmit Timestamp Reset Switch", FSL_SAI_TTCTL, + FSL_SAI_xTCTL_RTSC_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_SINGLE_EXT("Transmit Bit Counter Reset Switch", FSL_SAI_TTCTL, + FSL_SAI_xTCTL_RBC_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Transmit Timestamp Counter", FSL_SAI_TTCTN, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Transmit Bit Counter", FSL_SAI_TBCTN, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Transmit Latched Timestamp Counter", FSL_SAI_TTCAP, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_EXT("Receive Timestamp Control Switch", FSL_SAI_RTCTL, + FSL_SAI_xTCTL_TSEN_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_ENUM_EXT("Receive Timestamp Increment", receive_tstmp_enum, + fsl_asoc_get_enum_double, fsl_asoc_put_enum_double), + FSL_ASOC_SINGLE_EXT("Receive Timestamp Reset Switch", FSL_SAI_RTCTL, + FSL_SAI_xTCTL_RTSC_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_SINGLE_EXT("Receive Bit Counter Reset Switch", FSL_SAI_RTCTL, + FSL_SAI_xTCTL_RBC_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Receive Timestamp Counter", FSL_SAI_RTCTN, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Receive Bit Counter", FSL_SAI_RBCTN, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Receive Latched Timestamp Counter", FSL_SAI_RTCAP, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), +}; + /** * fsl_sai_dir_is_synced - Check if stream is synced by the opposite stream * @@ -1010,6 +1056,17 @@ static int fsl_sai_dai_resume(struct snd_soc_component *component) return 0; } +static int fsl_sai_component_probe(struct snd_soc_component *component) +{ + struct fsl_sai *sai = snd_soc_component_get_drvdata(component); + + if (sai->verid.feature & FSL_SAI_VERID_TSTMP_EN) + snd_soc_add_component_controls(component, fsl_sai_timestamp_ctrls, + ARRAY_SIZE(fsl_sai_timestamp_ctrls)); + + return 0; +} + static struct snd_soc_dai_driver fsl_sai_dai_template[] = { { .name = "sai-tx-rx", @@ -1063,6 +1120,7 @@ static struct snd_soc_dai_driver fsl_sai_dai_template[] = { static const struct snd_soc_component_driver fsl_component = { .name = "fsl-sai", + .probe = fsl_sai_component_probe, .resume = fsl_sai_dai_resume, .legacy_dai_naming = 1, }; @@ -1211,6 +1269,14 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) case FSL_SAI_RDR5: case FSL_SAI_RDR6: case FSL_SAI_RDR7: + case FSL_SAI_TTCTN: + case FSL_SAI_RTCTN: + case FSL_SAI_TTCTL: + case FSL_SAI_TBCTN: + case FSL_SAI_TTCAP: + case FSL_SAI_RTCTL: + case FSL_SAI_RBCTN: + case FSL_SAI_RTCAP: return true; default: return false; diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 7605cbaca3d8..af967833b6ed 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -196,9 +196,13 @@ #define FSL_SAI_MDIV_MASK 0xFFFFF /* SAI timestamp and bitcounter */ +#define FSL_SAI_xTCTL_TSEN_SHIFT 0 #define FSL_SAI_xTCTL_TSEN BIT(0) +#define FSL_SAI_xTCTL_TSINC_SHIFT 1 #define FSL_SAI_xTCTL_TSINC BIT(1) +#define FSL_SAI_xTCTL_RTSC_SHIFT 8 #define FSL_SAI_xTCTL_RTSC BIT(8) +#define FSL_SAI_xTCTL_RBC_SHIFT 9 #define FSL_SAI_xTCTL_RBC BIT(9) /* SAI type */ diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index d69a6b9795bf..7651c64bc837 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -10,6 +10,7 @@ #include <linux/clk-provider.h> #include <linux/module.h> #include <linux/of_address.h> +#include <linux/pm_runtime.h> #include <sound/soc.h> #include "fsl_utils.h" @@ -197,6 +198,136 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, } EXPORT_SYMBOL(fsl_asoc_constrain_rates); +/* + * Below functions are used by mixer interface to avoid accessing registers + * which are volatile at pm runtime suspend state (cache_only is enabled). + */ +int fsl_asoc_get_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_get_xr_sx(kcontrol, ucontrol); + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_get_xr_sx); + +int fsl_asoc_put_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_put_xr_sx(kcontrol, ucontrol); + /* + * As this function only used by the SNDRV_CTL_ELEM_ACCESS_VOLATILE + * case. return 0 to avoid control event notification. + */ + if (ret > 0) + ret = 0; + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_put_xr_sx); + +int fsl_asoc_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_get_enum_double); + +int fsl_asoc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + /* + * As this function only used by the SNDRV_CTL_ELEM_ACCESS_VOLATILE + * case. return 0 to avoid control event notification. + */ + if (ret > 0) + ret = 0; + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_put_enum_double); + +int fsl_asoc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_get_volsw(kcontrol, ucontrol); + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_get_volsw); + +int fsl_asoc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + /* + * As this function only used by the SNDRV_CTL_ELEM_ACCESS_VOLATILE + * case. return 0 to avoid control event notification. + */ + if (ret > 0) + ret = 0; + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_put_volsw); + MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); MODULE_DESCRIPTION("Freescale ASoC utility code"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h index 21b25a11ecda..1aab0c1cee62 100644 --- a/sound/soc/fsl/fsl_utils.h +++ b/sound/soc/fsl/fsl_utils.h @@ -31,4 +31,52 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, const struct snd_pcm_hw_constraint_list *original_constr, struct clk *pll8k_clk, struct clk *pll11k_clk, struct clk *ext_clk, int *target_rates); + +/* Similar to SOC_SINGLE_XR_SX, but it is for read only registers. */ +#define FSL_ASOC_SINGLE_XR_SX_EXT_RO(xname, xregbase, xregcount, xnbits, \ + xmin, xmax, xinvert, xhandler_get) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_soc_info_xr_sx, .get = xhandler_get, \ + .private_value = (unsigned long)&(struct soc_mreg_control) \ + {.regbase = xregbase, .regcount = xregcount, .nbits = xnbits, \ + .invert = xinvert, .min = xmin, .max = xmax} } + +/* Similar to SOC_SINGLE_EXT, but it is for volatile register. */ +#define FSL_ASOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_VOLATILE | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_SINGLE_VALUE(xreg, xshift, 0, xmax, xinvert, 0) } + +#define FSL_ASOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_VOLATILE | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_enum_double, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&xenum } + +int fsl_asoc_get_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_put_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + #endif /* _FSL_UTILS_H */ diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index a268fb81a2f8..ee16cf681488 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -62,6 +62,58 @@ struct fsl_xcvr { u32 spdif_constr_rates_list[SPDIF_NUM_RATES]; }; +static const char * const inc_mode[] = { + "On enabled and bitcount increment", "On enabled" +}; + +static SOC_ENUM_SINGLE_DECL(transmit_tstmp_enum, + FSL_XCVR_TX_DPTH_CNTR_CTRL, + FSL_XCVR_TX_DPTH_CNTR_CTRL_TSINC_SHIFT, inc_mode); +static SOC_ENUM_SINGLE_DECL(receive_tstmp_enum, + FSL_XCVR_RX_DPTH_CNTR_CTRL, + FSL_XCVR_RX_DPTH_CNTR_CTRL_TSINC_SHIFT, inc_mode); + +static const struct snd_kcontrol_new fsl_xcvr_timestamp_ctrls[] = { + FSL_ASOC_SINGLE_EXT("Transmit Timestamp Control Switch", FSL_XCVR_TX_DPTH_CNTR_CTRL, + FSL_XCVR_TX_DPTH_CNTR_CTRL_TSEN_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_ENUM_EXT("Transmit Timestamp Increment", transmit_tstmp_enum, + fsl_asoc_get_enum_double, fsl_asoc_put_enum_double), + FSL_ASOC_SINGLE_EXT("Transmit Timestamp Reset Switch", FSL_XCVR_TX_DPTH_CNTR_CTRL, + FSL_XCVR_TX_DPTH_CNTR_CTRL_RTSC_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_SINGLE_EXT("Transmit Bit Counter Reset Switch", FSL_XCVR_TX_DPTH_CNTR_CTRL, + FSL_XCVR_TX_DPTH_CNTR_CTRL_RBC_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Transmit Timestamp Counter", FSL_XCVR_TX_DPTH_TSCR, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Transmit Bit Counter", FSL_XCVR_TX_DPTH_BCR, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Transmit Bit Count Timestamp", FSL_XCVR_TX_DPTH_BCTR, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Transmit Latched Timestamp Counter", FSL_XCVR_TX_DPTH_BCRR, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_EXT("Receive Timestamp Control Switch", FSL_XCVR_RX_DPTH_CNTR_CTRL, + FSL_XCVR_RX_DPTH_CNTR_CTRL_TSEN_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_ENUM_EXT("Receive Timestamp Increment", receive_tstmp_enum, + fsl_asoc_get_enum_double, fsl_asoc_put_enum_double), + FSL_ASOC_SINGLE_EXT("Receive Timestamp Reset Switch", FSL_XCVR_RX_DPTH_CNTR_CTRL, + FSL_XCVR_RX_DPTH_CNTR_CTRL_RTSC_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_SINGLE_EXT("Receive Bit Counter Reset Switch", FSL_XCVR_RX_DPTH_CNTR_CTRL, + FSL_XCVR_RX_DPTH_CNTR_CTRL_RBC_SHIFT, 1, 0, + fsl_asoc_get_volsw, fsl_asoc_put_volsw), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Receive Timestamp Counter", FSL_XCVR_RX_DPTH_TSCR, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Receive Bit Counter", FSL_XCVR_RX_DPTH_BCR, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Receive Bit Count Timestamp", FSL_XCVR_RX_DPTH_BCTR, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), + FSL_ASOC_SINGLE_XR_SX_EXT_RO("Receive Latched Timestamp Counter", FSL_XCVR_RX_DPTH_BCRR, + 1, 32, 0, 0xffffffff, 0, fsl_asoc_get_xr_sx), +}; + static const struct fsl_xcvr_pll_conf { u8 mfi; /* min=0x18, max=0x38 */ u32 mfn; /* signed int, 2's compl., min=0x3FFF0000, max=0x00010000 */ @@ -115,10 +167,17 @@ static int fsl_xcvr_arc_mode_put(struct snd_kcontrol *kcontrol, struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int *item = ucontrol->value.enumerated.item; + int val = snd_soc_enum_item_to_val(e, item[0]); + int ret; - xcvr->arc_mode = snd_soc_enum_item_to_val(e, item[0]); + if (val < 0 || val > 1) + return -EINVAL; - return 0; + ret = (xcvr->arc_mode != val); + + xcvr->arc_mode = val; + + return ret; } static int fsl_xcvr_arc_mode_get(struct snd_kcontrol *kcontrol, @@ -218,10 +277,17 @@ static int fsl_xcvr_mode_put(struct snd_kcontrol *kcontrol, struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int *item = ucontrol->value.enumerated.item; + int val = snd_soc_enum_item_to_val(e, item[0]); struct snd_soc_card *card = dai->component->card; struct snd_soc_pcm_runtime *rtd; + int ret; - xcvr->mode = snd_soc_enum_item_to_val(e, item[0]); + if (val < FSL_XCVR_MODE_SPDIF || val > FSL_XCVR_MODE_EARC) + return -EINVAL; + + ret = (xcvr->mode != val); + + xcvr->mode = val; fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, (xcvr->mode == FSL_XCVR_MODE_ARC)); @@ -231,7 +297,7 @@ static int fsl_xcvr_mode_put(struct snd_kcontrol *kcontrol, rtd = snd_soc_get_pcm_runtime(card, card->dai_link); rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count = (xcvr->mode == FSL_XCVR_MODE_SPDIF ? 1 : 0); - return 0; + return ret; } static int fsl_xcvr_mode_get(struct snd_kcontrol *kcontrol, @@ -1070,8 +1136,20 @@ static struct snd_soc_dai_driver fsl_xcvr_dai = { }, }; +static int fsl_xcvr_component_probe(struct snd_soc_component *component) +{ + struct fsl_xcvr *xcvr = snd_soc_component_get_drvdata(component); + + snd_soc_component_init_regmap(component, xcvr->regmap); + + return 0; +} + static const struct snd_soc_component_driver fsl_xcvr_comp = { .name = "fsl-xcvr-dai", + .probe = fsl_xcvr_component_probe, + .controls = fsl_xcvr_timestamp_ctrls, + .num_controls = ARRAY_SIZE(fsl_xcvr_timestamp_ctrls), .legacy_dai_naming = 1, }; diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h index dade3945cc0c..0cc7945b1d9f 100644 --- a/sound/soc/fsl/fsl_xcvr.h +++ b/sound/soc/fsl/fsl_xcvr.h @@ -233,6 +233,24 @@ #define FSL_XCVR_TX_DPTH_CTRL_CLK_RATIO BIT(29) #define FSL_XCVR_TX_DPTH_CTRL_TM_NO_PRE_BME GENMASK(31, 30) +#define FSL_XCVR_RX_DPTH_CNTR_CTRL_TSEN_SHIFT 0 +#define FSL_XCVR_RX_DPTH_CNTR_CTRL_TSEN BIT(0) +#define FSL_XCVR_RX_DPTH_CNTR_CTRL_TSINC_SHIFT 1 +#define FSL_XCVR_RX_DPTH_CNTR_CTRL_TSINC BIT(1) +#define FSL_XCVR_RX_DPTH_CNTR_CTRL_RBC_SHIFT 8 +#define FSL_XCVR_RX_DPTH_CNTR_CTRL_RBC BIT(8) +#define FSL_XCVR_RX_DPTH_CNTR_CTRL_RTSC_SHIFT 9 +#define FSL_XCVR_RX_DPTH_CNTR_CTRL_RTSC BIT(9) + +#define FSL_XCVR_TX_DPTH_CNTR_CTRL_TSEN_SHIFT 0 +#define FSL_XCVR_TX_DPTH_CNTR_CTRL_TSEN BIT(0) +#define FSL_XCVR_TX_DPTH_CNTR_CTRL_TSINC_SHIFT 1 +#define FSL_XCVR_TX_DPTH_CNTR_CTRL_TSINC BIT(1) +#define FSL_XCVR_TX_DPTH_CNTR_CTRL_RBC_SHIFT 8 +#define FSL_XCVR_TX_DPTH_CNTR_CTRL_RBC BIT(8) +#define FSL_XCVR_TX_DPTH_CNTR_CTRL_RTSC_SHIFT 9 +#define FSL_XCVR_TX_DPTH_CNTR_CTRL_RTSC BIT(9) + #define FSL_XCVR_PHY_AI_CTRL_AI_RESETN BIT(15) #define FSL_XCVR_PHY_AI_CTRL_AI_RWB BIT(31) diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index d51a3de493cb..e0fb4f8fd522 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -273,8 +273,8 @@ static const struct snd_soc_component_driver imx_soc_component_fiq = { .prepare = snd_imx_pcm_prepare, .trigger = snd_imx_pcm_trigger, .pointer = snd_imx_pcm_pointer, - .pcm_construct = snd_imx_pcm_new, - .pcm_destruct = snd_imx_pcm_free, + .pcm_new = snd_imx_pcm_new, + .pcm_free = snd_imx_pcm_free, }; int imx_pcm_fiq_init(struct platform_device *pdev, diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index edab68ae8366..031e5272215d 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -624,7 +624,7 @@ static int imx_rpmsg_pcm_new(struct snd_soc_component *component, static const struct snd_soc_component_driver imx_rpmsg_soc_component = { .name = IMX_PCM_DRV_NAME, - .pcm_construct = imx_rpmsg_pcm_new, + .pcm_new = imx_rpmsg_pcm_new, .open = imx_rpmsg_pcm_open, .close = imx_rpmsg_pcm_close, .hw_params = imx_rpmsg_pcm_hw_params, diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 76a8e68c1b62..40e0043cfe15 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -30,6 +30,53 @@ static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets[] = { SND_SOC_DAPM_MIC("Main MIC", NULL), }; +static int imx_rpmsg_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); + snd_pcm_format_t format = params_format(params); + struct device *dev = rtd->card->dev; + unsigned int fmt = rtd->dai_link->dai_fmt; + bool format_is_dsd = false; + int ret; + + switch (format) { + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U16_BE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + case SNDRV_PCM_FORMAT_DSD_U32_BE: + format_is_dsd = true; + break; + default: + format_is_dsd = false; + break; + } + + if (format_is_dsd) + fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | + SND_SOC_DAIFMT_PDM; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set cpu dai fmt: %d\n", ret); + return ret; + } + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set codec dai fmt: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct snd_soc_ops imx_rpmsg_ops = { + .hw_params = imx_rpmsg_hw_params, +}; + static int imx_rpmsg_late_probe(struct snd_soc_card *card) { struct imx_rpmsg *data = snd_soc_card_get_drvdata(card); @@ -135,6 +182,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev) data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + data->dai.ops = &imx_rpmsg_ops; /* * i.MX rpmsg sound cards work on codec slave mode. MCLK will be diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index a593a95aa532..56e2cf2f727b 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -307,7 +307,7 @@ static const struct snd_soc_component_driver mpc5200_audio_dma_component = { .close = psc_dma_close, .pointer = psc_dma_pointer, .trigger = psc_dma_trigger, - .pcm_construct = psc_dma_new, + .pcm_new = psc_dma_new, }; int mpc5200_audio_dma_create(struct platform_device *op) @@ -326,7 +326,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) dev_err(&op->dev, "Missing reg property\n"); return -ENODEV; } - regs = ioremap(res.start, resource_size(&res)); + regs = devm_ioremap(&op->dev, res.start, resource_size(&res)); if (!regs) { dev_err(&op->dev, "Could not map registers\n"); return -ENODEV; @@ -334,10 +334,8 @@ int mpc5200_audio_dma_create(struct platform_device *op) /* Allocate and initialize the driver private data */ psc_dma = kzalloc_obj(*psc_dma); - if (!psc_dma) { - ret = -ENOMEM; - goto out_unmap; - } + if (!psc_dma) + return -ENOMEM; /* Get the PSC ID */ prop = of_get_property(op->dev.of_node, "cell-index", &size); @@ -424,8 +422,6 @@ out_irq: free_irq(psc_dma->playback.irq, &psc_dma->playback); out_free: kfree(psc_dma); -out_unmap: - iounmap(regs); return ret; } EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); @@ -444,7 +440,6 @@ int mpc5200_audio_dma_destroy(struct platform_device *op) free_irq(psc_dma->capture.irq, &psc_dma->capture); free_irq(psc_dma->playback.irq, &psc_dma->playback); - iounmap(psc_dma->psc_regs); kfree(psc_dma); dev_set_drvdata(&op->dev, NULL); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 8a5f41704739..18ce4ee06350 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -76,7 +76,7 @@ static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc) { struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); - if (dai && (dai->component->driver->pcm_construct || + if (dai && (dai->component->driver->pcm_new || (dai->driver->ops && dai->driver->ops->pcm_new))) return true; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 89d694c2cbdd..e5cb602fd248 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -468,6 +468,7 @@ int simple_util_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *sdai; struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); + enum simple_util_sysclk_order order = props->sysclk_order; unsigned int mclk, mclk_fs = 0; int i, ret; @@ -501,18 +502,36 @@ int simple_util_hw_params(struct snd_pcm_substream *substream, goto end; } - for_each_rtd_codec_dais(rtd, i, sdai) { - pdai = simple_props_to_dai_codec(props, i); - ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); - if (ret && ret != -ENOTSUPP) - goto end; - } + if (order == SIMPLE_SYSCLK_ORDER_CPU_FIRST) { + /* CPU first */ + for_each_rtd_cpu_dais(rtd, i, sdai) { + pdai = simple_props_to_dai_cpu(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); + if (ret && ret != -ENOTSUPP) + goto end; + } - for_each_rtd_cpu_dais(rtd, i, sdai) { - pdai = simple_props_to_dai_cpu(props, i); - ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); - if (ret && ret != -ENOTSUPP) - goto end; + for_each_rtd_codec_dais(rtd, i, sdai) { + pdai = simple_props_to_dai_codec(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); + if (ret && ret != -ENOTSUPP) + goto end; + } + } else { + /* default: codec first */ + for_each_rtd_codec_dais(rtd, i, sdai) { + pdai = simple_props_to_dai_codec(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); + if (ret && ret != -ENOTSUPP) + goto end; + } + + for_each_rtd_cpu_dais(rtd, i, sdai) { + pdai = simple_props_to_dai_cpu(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); + if (ret && ret != -ENOTSUPP) + goto end; + } } } @@ -699,7 +718,7 @@ void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, int is_single_links) { /* - * In soc_bind_dai_link() will check cpu name after + * In snd_soc_add_pcm_runtime() will check cpu name after * of_node matching if dai_link has cpu_dai_name. * but, it will never match if name was created by * fmt_single_name() remove cpu_dai_name if cpu_args @@ -1109,7 +1128,9 @@ int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep, struct device *dev = simple_priv_to_dev(priv); struct device_node *node; struct of_phandle_args args = {}; + struct snd_soc_dai_link_component resolved_dlc = {}; struct snd_soc_dai *dai; + const char *fallback_dai_name; int ret; if (!ep) @@ -1133,39 +1154,31 @@ int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep, dlc->of_node = node; dlc->dai_name = dai_name; dlc->dai_args = dai_args; + } else { + /* Get dai->name */ + args.np = node; + args.args[0] = graph_get_dai_id(ep); + args.args_count = (of_graph_get_endpoint_count(node) > 1); - goto parse_dai_end; - } + ret = snd_soc_get_dlc(&args, &resolved_dlc); + if (ret < 0) + goto err; - /* Get dai->name */ - args.np = node; - args.args[0] = graph_get_dai_id(ep); - args.args_count = (of_graph_get_endpoint_count(node) > 1); + /* Keep fallback dai_name valid across component rebind */ + fallback_dai_name = resolved_dlc.dai_name; + if (fallback_dai_name) { + fallback_dai_name = devm_kstrdup_const(dev, fallback_dai_name, + GFP_KERNEL); + ret = -ENOMEM; + if (!fallback_dai_name) + goto err; + } - /* - * FIXME - * - * Here, dlc->dai_name is pointer to CPU/Codec DAI name. - * If user unbinded CPU or Codec driver, but not for Sound Card, - * dlc->dai_name is keeping unbinded CPU or Codec - * driver's pointer. - * - * If user re-bind CPU or Codec driver again, ALSA SoC will try - * to rebind Card via snd_soc_try_rebind_card(), but because of - * above reason, it might can't bind Sound Card. - * Because Sound Card is pointing to released dai_name pointer. - * - * To avoid this rebind Card issue, - * 1) It needs to alloc memory to keep dai_name eventhough - * CPU or Codec driver was unbinded, or - * 2) user need to rebind Sound Card everytime - * if he unbinded CPU or Codec. - */ - ret = snd_soc_get_dlc(&args, dlc); - if (ret < 0) - goto err; + dlc->of_node = resolved_dlc.of_node; + dlc->dai_name = fallback_dai_name; + dlc->dai_args = resolved_dlc.dai_args; + } -parse_dai_end: if (is_single_link) *is_single_link = of_graph_get_endpoint_count(node) == 1; ret = 0; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 06638f9a74b8..b24ba1330896 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -48,7 +48,8 @@ static int simple_parse_platform(struct simple_util_priv *priv, /* * Get node via "sound-dai = <&phandle port>" - * it will be used as xxx_of_node on soc_bind_dai_link() + * It will be used as the of_node for component matching during + * snd_soc_add_pcm_runtime(). */ ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); if (ret) @@ -68,7 +69,9 @@ static int simple_parse_dai(struct simple_util_priv *priv, { struct device *dev = simple_priv_to_dev(priv); struct of_phandle_args args; + struct snd_soc_dai_link_component resolved_dlc = {}; struct snd_soc_dai *dai; + const char *fallback_dai_name; int ret; if (!node) @@ -76,7 +79,8 @@ static int simple_parse_dai(struct simple_util_priv *priv, /* * Get node via "sound-dai = <&phandle port>" - * it will be used as xxx_of_node on soc_bind_dai_link() + * It will be used as the of_node for component matching during + * snd_soc_add_pcm_runtime(). */ ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); if (ret) @@ -92,34 +96,28 @@ static int simple_parse_dai(struct simple_util_priv *priv, dlc->dai_args = snd_soc_copy_dai_args(dev, &args); if (!dlc->dai_args) goto end; + } else { + ret = snd_soc_get_dlc(&args, &resolved_dlc); + if (ret < 0) + goto end; - goto parse_dai_end; - } + /* Keep fallback dai_name valid across component rebind */ + fallback_dai_name = resolved_dlc.dai_name; + if (fallback_dai_name) { + fallback_dai_name = devm_kstrdup_const(dev, fallback_dai_name, + GFP_KERNEL); + ret = -ENOMEM; + if (!fallback_dai_name) { + of_node_put(resolved_dlc.of_node); + goto end; + } + } - /* - * FIXME - * - * Here, dlc->dai_name is pointer to CPU/Codec DAI name. - * If user unbinded CPU or Codec driver, but not for Sound Card, - * dlc->dai_name is keeping unbinded CPU or Codec - * driver's pointer. - * - * If user re-bind CPU or Codec driver again, ALSA SoC will try - * to rebind Card via snd_soc_try_rebind_card(), but because of - * above reason, it might can't bind Sound Card. - * Because Sound Card is pointing to released dai_name pointer. - * - * To avoid this rebind Card issue, - * 1) It needs to alloc memory to keep dai_name eventhough - * CPU or Codec driver was unbinded, or - * 2) user need to rebind Sound Card everytime - * if he unbinded CPU or Codec. - */ - ret = snd_soc_get_dlc(&args, dlc); - if (ret < 0) - goto end; + dlc->of_node = resolved_dlc.of_node; + dlc->dai_name = fallback_dai_name; + dlc->dai_args = resolved_dlc.dai_args; + } -parse_dai_end: if (is_single_link) *is_single_link = !args.args_count; ret = 0; diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c index 2e49066dedd4..fc40d024152e 100644 --- a/sound/soc/generic/test-component.c +++ b/sound/soc/generic/test-component.c @@ -273,8 +273,8 @@ static int test_component_resume(struct snd_soc_component *component) } #define PREALLOC_BUFFER (32 * 1024) -static int test_component_pcm_construct(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) +static int test_component_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { mile_stone(component); @@ -287,8 +287,8 @@ static int test_component_pcm_construct(struct snd_soc_component *component, return 0; } -static void test_component_pcm_destruct(struct snd_soc_component *component, - struct snd_pcm *pcm) +static void test_component_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { mile_stone(component); } @@ -562,7 +562,7 @@ static int test_driver_probe(struct platform_device *pdev) if (adata->is_cpu) { cdriv->name = "test_cpu"; - cdriv->pcm_construct = test_component_pcm_construct; + cdriv->pcm_new = test_component_pcm_new; cdriv->pointer = test_component_pointer; cdriv->trigger = test_component_trigger; cdriv->legacy_dai_naming = 1; @@ -597,7 +597,7 @@ static int test_driver_probe(struct platform_device *pdev) cdriv->be_hw_params_fixup = test_component_be_hw_params_fixup; if (adata->is_cpu) - cdriv->pcm_destruct = test_component_pcm_destruct; + cdriv->pcm_free = test_component_pcm_free; } i = 0; diff --git a/sound/soc/google/chv3-i2s.c b/sound/soc/google/chv3-i2s.c index 0ff24653d49f..fd12cedfe1c9 100644 --- a/sound/soc/google/chv3-i2s.c +++ b/sound/soc/google/chv3-i2s.c @@ -163,8 +163,8 @@ static int chv3_dma_close(struct snd_soc_component *component, return 0; } -static int chv3_dma_pcm_construct(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) +static int chv3_dma_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); struct snd_pcm_substream *substream; @@ -271,7 +271,7 @@ static const struct snd_soc_component_driver chv3_i2s_comp = { .name = "chv3-i2s-comp", .open = chv3_dma_open, .close = chv3_dma_close, - .pcm_construct = chv3_dma_pcm_construct, + .pcm_new = chv3_dma_pcm_new, .hw_params = chv3_dma_hw_params, .prepare = chv3_dma_prepare, .pointer = chv3_dma_pointer, diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 67caea39b557..f074af2499c8 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -707,7 +707,7 @@ static const struct snd_soc_component_driver sst_soc_platform_drv = { .pointer = sst_soc_pointer, .delay = sst_soc_delay, .compress_ops = &sst_platform_compress_ops, - .pcm_construct = sst_soc_pcm_new, + .pcm_new = sst_soc_pcm_new, }; static int sst_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index d53c2f76fcd4..797b9c9163b4 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -1349,8 +1349,8 @@ static int avs_component_mmap(struct snd_soc_component *component, #define MAX_PREALLOC_SIZE (32 * 1024 * 1024) -static int avs_component_construct(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) +static int avs_component_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0); struct snd_pcm *pcm = rtd->pcm; @@ -1377,7 +1377,7 @@ static struct snd_soc_component_driver avs_component_driver = { .open = avs_component_open, .pointer = avs_component_pointer, .mmap = avs_component_mmap, - .pcm_construct = avs_component_construct, + .pcm_new = avs_component_new, .module_get_upon_open = 1, /* increment refcount when a pcm is opened */ .topology_name_prefix = "intel/avs", }; @@ -1755,7 +1755,7 @@ static struct snd_soc_component_driver avs_hda_component_driver = { .open = avs_component_hda_open, .pointer = avs_component_pointer, .mmap = avs_component_mmap, - .pcm_construct = avs_component_construct, + .pcm_new = avs_component_new, /* * hda platform component's probe() is dependent on * codec->pcm_list_head, it needs to be initialized after codec diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 74096236984a..099119ad28b3 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -144,7 +144,7 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream, ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); if (ret < 0) return ret; - bps = snd_pcm_format_physical_width(params->codec.format); + bps = snd_pcm_format_physical_width((__force snd_pcm_format_t)params->codec.format); if (bps < 0) return bps; format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate); diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 103e0b445603..40da3eea5fa7 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -285,10 +285,12 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_ON(event)) { ret = clk_prepare_enable(priv->mclk); if (ret < 0) { - dev_err(card->dev, "could not configure MCLK state\n"); + dev_err(card->dev, "could not configure MCLK state: %d\n", ret); return ret; } ret = byt_rt5640_prepare_and_enable_pll1(codec_dai, 48000); + if (ret < 0) + clk_disable_unprepare(priv->mclk); } else { /* * Set codec clock source to internal clock before diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 68cf463f1d50..62cb4856c797 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -205,10 +205,12 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_ON(event)) { ret = clk_prepare_enable(priv->mclk); if (ret < 0) { - dev_err(card->dev, "could not configure MCLK state"); + dev_err(card->dev, "could not configure MCLK state: %d\n", ret); return ret; } ret = byt_rt5651_prepare_and_enable_pll1(codec_dai, 48000, 50); + if (ret < 0) + clk_disable_unprepare(priv->mclk); } else { /* * Set codec clock source to internal clock before diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 359723f2700e..fd4cefd298d2 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -67,7 +67,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, ret = clk_prepare_enable(ctx->mclk); if (ret < 0) { dev_err(card->dev, - "could not configure MCLK state"); + "could not configure MCLK state: %d\n", ret); return ret; } } @@ -77,6 +77,8 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, CHT_PLAT_CLK_3_HZ, 48000 * 512); if (ret < 0) { dev_err(card->dev, "can't set codec pll: %d\n", ret); + if (ctx->mclk) + clk_disable_unprepare(ctx->mclk); return ret; } @@ -85,6 +87,8 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, 48000 * 512, SND_SOC_CLOCK_IN); if (ret < 0) { dev_err(card->dev, "can't set codec sysclk: %d\n", ret); + if (ctx->mclk) + clk_disable_unprepare(ctx->mclk); return ret; } } else { diff --git a/sound/soc/intel/boards/ehl_rt5660.c b/sound/soc/intel/boards/ehl_rt5660.c index c40cd9fb1a26..7de39bc1e763 100644 --- a/sound/soc/intel/boards/ehl_rt5660.c +++ b/sound/soc/intel/boards/ehl_rt5660.c @@ -23,10 +23,8 @@ #include "hda_dsp_common.h" #include "../../codecs/rt5660.h" -#define DUAL_CHANNEL 2 #define HDMI_LINK_START 3 #define HDMI_LINE_END 6 -#define NAME_SIZE 32 #define IDISP_CODEC_MASK 0x4 struct sof_card_private { diff --git a/sound/soc/intel/catpt/Makefile b/sound/soc/intel/catpt/Makefile index f5f6a7e956ce..e8316e33b820 100644 --- a/sound/soc/intel/catpt/Makefile +++ b/sound/soc/intel/catpt/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-catpt-y := device.o dsp.o loader.o ipc.o messages.o pcm.o sysfs.o # tell define_trace.h where to find the trace header diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index df8a5fd95e13..7e479ef89ad0 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -96,7 +96,7 @@ struct catpt_dev { struct catpt_module_type modules[CATPT_MODULE_COUNT]; struct catpt_ssp_device_format devfmt[CATPT_SSP_COUNT]; struct list_head stream_list; - spinlock_t list_lock; + struct mutex stream_mutex; struct mutex clk_mutex; struct catpt_dx_context dx_ctx; diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index 0b3f20e384c7..ca4fd18b6a6e 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -226,7 +226,7 @@ static void catpt_dev_init(struct catpt_dev *cdev, struct device *dev, cdev->spec = spec; init_completion(&cdev->fw_ready); INIT_LIST_HEAD(&cdev->stream_list); - spin_lock_init(&cdev->list_lock); + mutex_init(&cdev->stream_mutex); mutex_init(&cdev->clk_mutex); /* diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c index 5a01a9afb26e..2e3b7a5cbb9b 100644 --- a/sound/soc/intel/catpt/ipc.c +++ b/sound/soc/intel/catpt/ipc.c @@ -5,6 +5,7 @@ // Author: Cezary Rojewski <cezary.rojewski@intel.com> // +#include <linux/cleanup.h> #include <linux/irqreturn.h> #include "core.h" #include "messages.h" @@ -151,6 +152,8 @@ catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg) struct catpt_notify_position pos; struct catpt_notify_glitch glitch; + guard(mutex)(&cdev->stream_mutex); + stream = catpt_stream_find(cdev, msg.stream_hw_id); if (!stream) { dev_warn(cdev->dev, "notify %d for non-existent stream %d\n", diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index dc7afe587e6f..432cb1f0ab4e 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -90,6 +90,7 @@ int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) { struct catpt_stream_runtime *stream; + /* Lockless as no streams can be added or removed during D3 -> D0 transition. */ list_for_each_entry(stream, &cdev->stream_list, node) { u32 off, size; int ret; @@ -180,6 +181,7 @@ catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) { struct catpt_stream_runtime *stream; + /* Lockless as no streams can be added or removed during D3 -> D0 transition. */ list_for_each_entry(stream, &cdev->stream_list, node) { u32 off, size; int ret; diff --git a/sound/soc/intel/catpt/messages.h b/sound/soc/intel/catpt/messages.h index a634943eb669..fcc9f1b46bc2 100644 --- a/sound/soc/intel/catpt/messages.h +++ b/sound/soc/intel/catpt/messages.h @@ -69,6 +69,7 @@ struct catpt_fw_version { int catpt_ipc_get_fw_version(struct catpt_dev *cdev, struct catpt_fw_version *version); +/* PIN_IDs represent both, individual streams and the general mixer. */ enum catpt_pin_id { CATPT_PIN_ID_SYSTEM = 0, CATPT_PIN_ID_REFERENCE = 1, @@ -79,6 +80,8 @@ enum catpt_pin_id { CATPT_PIN_ID_MIXER = 7, CATPT_PIN_ID_BLUETOOTH_CAPTURE = 8, CATPT_PIN_ID_BLUETOOTH_RENDER = 9, + /* 10 is reserved */ + CATPT_PIN_ID_INVALID = 11, }; enum catpt_path_id { diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 2c5ea4e0ff3d..7b2bab12c707 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -5,6 +5,7 @@ // Author: Cezary Rojewski <cezary.rojewski@intel.com> // +#include <linux/cleanup.h> #include <linux/pm_runtime.h> #include <sound/soc.h> #include <sound/pcm_params.h> @@ -97,12 +98,12 @@ catpt_get_stream_template(struct snd_pcm_substream *substream) return catpt_topology[type]; } +/* Caller responsible for holding ->stream_mutex. */ struct catpt_stream_runtime * catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id) { struct catpt_stream_runtime *pos, *result = NULL; - spin_lock(&cdev->list_lock); list_for_each_entry(pos, &cdev->stream_list, node) { if (pos->info.stream_hw_id == stream_hw_id) { result = pos; @@ -110,40 +111,53 @@ catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id) } } - spin_unlock(&cdev->list_lock); return result; } -static void catpt_stream_read_position(struct catpt_dev *cdev, - struct catpt_stream_runtime *stream, u32 *pos) +/* Caller responsible for holding ->stream_mutex. */ +static u8 catpt_stream_hw_id(struct catpt_dev *cdev, enum catpt_pin_id pin_id) { - memcpy_fromio(pos, cdev->lpe_ba + stream->info.read_pos_regaddr, sizeof(*pos)); + struct catpt_stream_runtime *stream; + + switch (pin_id) { + default: + stream = catpt_stream_find(cdev, pin_id); + if (stream) + return stream->info.stream_hw_id; + break; + case CATPT_PIN_ID_MIXER: + if (!list_empty(&cdev->stream_list)) + return cdev->mixer.mixer_hw_id; + break; + } + + return CATPT_PIN_ID_INVALID; } -static u32 catpt_stream_volume(struct catpt_dev *cdev, - struct catpt_stream_runtime *stream, u32 channel) +/* Caller responsible for holding ->stream_mutex. */ +static u32 *catpt_stream_volume_regs(struct catpt_dev *cdev, enum catpt_pin_id pin_id) { - u32 volume, offset; + struct catpt_stream_runtime *stream; - if (channel >= CATPT_CHANNELS_MAX) - channel = 0; + switch (pin_id) { + case CATPT_PIN_ID_MIXER: + if (!list_empty(&cdev->stream_list)) + return &cdev->mixer.volume_regaddr[0]; + break; + default: + stream = catpt_stream_find(cdev, pin_id); + if (stream) + return &stream->info.volume_regaddr[0]; + break; + } - offset = stream->info.volume_regaddr[channel]; - memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume)); - return volume; + return NULL; } -static u32 catpt_mixer_volume(struct catpt_dev *cdev, - struct catpt_mixer_stream_info *info, u32 channel) +static void catpt_stream_read_position(struct catpt_dev *cdev, + struct catpt_stream_runtime *stream, u32 *pos) { - u32 volume, offset; - - if (channel >= CATPT_CHANNELS_MAX) - channel = 0; - - offset = info->volume_regaddr[channel]; - memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume)); - return volume; + memcpy_fromio(pos, cdev->lpe_ba + stream->info.read_pos_regaddr, sizeof(*pos)); } static void catpt_arrange_page_table(struct snd_pcm_substream *substream, @@ -286,10 +300,6 @@ static int catpt_dai_startup(struct snd_pcm_substream *substream, INIT_LIST_HEAD(&stream->node); snd_soc_dai_set_dma_data(dai, substream, stream); - spin_lock(&cdev->list_lock); - list_add_tail(&stream->node, &cdev->stream_list); - spin_unlock(&cdev->list_lock); - return 0; err_request: @@ -307,10 +317,6 @@ static void catpt_dai_shutdown(struct snd_pcm_substream *substream, stream = snd_soc_dai_get_dma_data(dai, substream); - spin_lock(&cdev->list_lock); - list_del(&stream->node); - spin_unlock(&cdev->list_lock); - release_resource(stream->persistent); kfree(stream->persistent); catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); @@ -322,46 +328,62 @@ static void catpt_dai_shutdown(struct snd_pcm_substream *substream, static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol); -static int catpt_dai_apply_usettings(struct snd_soc_dai *dai, - struct catpt_stream_runtime *stream) +struct catpt_control_data { + enum catpt_pin_id pin_id; + long volumes[CATPT_CHANNELS_MAX]; +}; + +static int catpt_apply_volume(struct catpt_dev *cdev, struct snd_soc_card *card, const char *name) +{ + struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card->snd_card, name); + struct catpt_control_data *data; + + if (!kctl) + return -ENOENT; + data = (struct catpt_control_data *)kctl->private_value; + + return catpt_set_dspvol(cdev, data->pin_id, data->volumes); +} + +static int catpt_apply_mute(struct catpt_dev *cdev, struct snd_soc_card *card) +{ + struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card->snd_card, "Loopback Mute"); + bool mute; + int ret; + + if (!kctl) + return -ENOENT; + mute = *(bool *)kctl->private_value; + + ret = catpt_ipc_mute_loopback(cdev, CATPT_PIN_ID_REFERENCE, mute); + return CATPT_IPC_RET(ret); +} + +static int catpt_apply_controls(struct catpt_dev *cdev, struct snd_soc_card *card, + struct catpt_stream_runtime *stream) { - struct snd_soc_component *component = dai->component; - struct snd_kcontrol *pos; - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); - const char *name; int ret; - u32 id = stream->info.stream_hw_id; - /* only selected streams have individual controls */ - switch (id) { + /* Update the master volume when the first stream is opened. */ + if (list_empty(&cdev->stream_list)) { + ret = catpt_apply_volume(cdev, card, "Master Playback Volume"); + if (ret) + return ret; + } + + /* Only selected streams have individual controls. */ + switch (stream->info.stream_hw_id) { case CATPT_PIN_ID_OFFLOAD1: - name = "Media0 Playback Volume"; - break; + return catpt_apply_volume(cdev, card, "Media0 Playback Volume"); case CATPT_PIN_ID_OFFLOAD2: - name = "Media1 Playback Volume"; - break; + return catpt_apply_volume(cdev, card, "Media1 Playback Volume"); case CATPT_PIN_ID_CAPTURE1: - name = "Mic Capture Volume"; - break; + return catpt_apply_volume(cdev, card, "Mic Capture Volume"); case CATPT_PIN_ID_REFERENCE: - name = "Loopback Mute"; - break; + return catpt_apply_mute(cdev, card); default: return 0; } - - list_for_each_entry(pos, &component->card->snd_card->controls, list) { - if (pos->private_data == component && - !strncmp(name, pos->id.name, sizeof(pos->id.name))) - break; - } - if (list_entry_is_head(pos, &component->card->snd_card->controls, list)) - return -ENOENT; - - if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK) - return catpt_set_dspvol(cdev, id, (long *)pos->private_value); - ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)pos->private_value); - return CATPT_IPC_RET(ret); } static int catpt_dai_hw_params(struct snd_pcm_substream *substream, @@ -410,12 +432,15 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, if (ret) return CATPT_IPC_RET(ret); - ret = catpt_dai_apply_usettings(dai, stream); + guard(mutex)(&cdev->stream_mutex); + + ret = catpt_apply_controls(cdev, dai->component->card, stream); if (ret) { catpt_ipc_free_stream(cdev, stream->info.stream_hw_id); return ret; } + list_add_tail(&stream->node, &cdev->stream_list); stream->allocated = true; return 0; } @@ -430,6 +455,10 @@ static int catpt_dai_hw_free(struct snd_pcm_substream *substream, if (!stream->allocated) return 0; + mutex_lock(&cdev->stream_mutex); + list_del(&stream->node); + mutex_unlock(&cdev->stream_mutex); + catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id); catpt_ipc_free_stream(cdev, stream->info.stream_hw_id); @@ -574,8 +603,8 @@ static const struct snd_pcm_hardware catpt_pcm_hardware = { .buffer_bytes_max = CATPT_BUFFER_MAX_SIZE, }; -static int catpt_component_pcm_construct(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtm) +static int catpt_component_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtm) { struct catpt_dev *cdev = dev_get_drvdata(component->dev); @@ -856,187 +885,91 @@ static int catpt_volume_info(struct snd_kcontrol *kcontrol, return 0; } -static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int catpt_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kctl); struct catpt_dev *cdev = dev_get_drvdata(component->dev); - u32 dspvol; - int ret; + struct catpt_control_data *data; + u32 dspvol, *regs; + long *uvolumes; int i; - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; + data = (struct catpt_control_data *)kctl->private_value; + uvolumes = &uctl->value.integer.value[0]; - for (i = 0; i < CATPT_CHANNELS_MAX; i++) { - dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i); - ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); - } + guard(mutex)(&cdev->stream_mutex); - pm_runtime_put_autosuspend(cdev->dev); + regs = catpt_stream_volume_regs(cdev, data->pin_id); + if (regs) { + for (i = 0; i < CATPT_CHANNELS_MAX; i++) { + dspvol = readl(cdev->lpe_ba + regs[i]); + data->volumes[i] = dspvol_to_ctlvol(dspvol); + } + } + memcpy(uvolumes, data->volumes, sizeof(data->volumes)); return 0; } -static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int catpt_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kctl); struct catpt_dev *cdev = dev_get_drvdata(component->dev); + struct catpt_control_data *data; + u8 stream_hw_id; + long *uvolumes; int ret; - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; + data = (struct catpt_control_data *)kctl->private_value; + uvolumes = &uctl->value.integer.value[0]; - ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id, - ucontrol->value.integer.value); - - pm_runtime_put_autosuspend(cdev->dev); - - return ret; -} - -static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - enum catpt_pin_id pin_id) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct catpt_stream_runtime *stream; - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - long *ctlvol = (long *)kcontrol->private_value; - u32 dspvol; - int ret; - int i; - - stream = catpt_stream_find(cdev, pin_id); - if (!stream) { - for (i = 0; i < CATPT_CHANNELS_MAX; i++) - ucontrol->value.integer.value[i] = ctlvol[i]; + if (!memcmp(data->volumes, uvolumes, sizeof(data->volumes))) return 0; - } - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; + guard(mutex)(&cdev->stream_mutex); - for (i = 0; i < CATPT_CHANNELS_MAX; i++) { - dspvol = catpt_stream_volume(cdev, stream, i); - ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); + stream_hw_id = catpt_stream_hw_id(cdev, data->pin_id); + if (stream_hw_id != CATPT_PIN_ID_INVALID) { + ret = catpt_set_dspvol(cdev, stream_hw_id, uvolumes); + if (ret) + return ret; } - pm_runtime_put_autosuspend(cdev->dev); - - return 0; + memcpy(data->volumes, uvolumes, sizeof(data->volumes)); + return 1; } -static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - enum catpt_pin_id pin_id) +static int catpt_loopback_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct catpt_stream_runtime *stream; - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - long *ctlvol = (long *)kcontrol->private_value; - int ret, i; - - stream = catpt_stream_find(cdev, pin_id); - if (!stream) { - for (i = 0; i < CATPT_CHANNELS_MAX; i++) - ctlvol[i] = ucontrol->value.integer.value[i]; - return 0; - } - - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; - - ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id, - ucontrol->value.integer.value); - - pm_runtime_put_autosuspend(cdev->dev); - - if (ret) - return ret; - - for (i = 0; i < CATPT_CHANNELS_MAX; i++) - ctlvol[i] = ucontrol->value.integer.value[i]; + uctl->value.integer.value[0] = *(bool *)kctl->private_value; return 0; } -static int catpt_offload1_volume_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) +static int catpt_loopback_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { - return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1); -} - -static int catpt_offload1_volume_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1); -} - -static int catpt_offload2_volume_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2); -} - -static int catpt_offload2_volume_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2); -} - -static int catpt_capture_volume_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1); -} - -static int catpt_capture_volume_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1); -} - -static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value; - return 0; -} - -static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct catpt_stream_runtime *stream; + struct snd_soc_component *component = snd_kcontrol_chip(kctl); struct catpt_dev *cdev = dev_get_drvdata(component->dev); - bool mute; + bool *kmute, cmute; + u8 stream_hw_id; int ret; - mute = (bool)ucontrol->value.integer.value[0]; - stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE); - if (!stream) { - *(bool *)kcontrol->private_value = mute; - return 0; - } - - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; + kmute = (bool *)kctl->private_value; + cmute = (bool)uctl->value.integer.value[0]; - ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute); + if (*kmute == cmute) + return 0; - pm_runtime_put_autosuspend(cdev->dev); + guard(mutex)(&cdev->stream_mutex); - if (ret) - return CATPT_IPC_RET(ret); + stream_hw_id = catpt_stream_hw_id(cdev, CATPT_PIN_ID_REFERENCE); + if (stream_hw_id != CATPT_PIN_ID_INVALID) { + ret = catpt_ipc_mute_loopback(cdev, stream_hw_id, cmute); + if (ret) + return CATPT_IPC_RET(ret); + } - *(bool *)kcontrol->private_value = mute; - return 0; + *kmute = cmute; + return 1; } static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol, @@ -1067,27 +1000,28 @@ static int catpt_waves_param_put(struct snd_kcontrol *kcontrol, static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1); -#define CATPT_VOLUME_CTL(kname, sname) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = (kname), \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .info = catpt_volume_info, \ - .get = catpt_##sname##_volume_get, \ - .put = catpt_##sname##_volume_put, \ - .tlv.p = catpt_volume_tlv, \ - .private_value = (unsigned long) \ - &(long[CATPT_CHANNELS_MAX]) {0} } +#define CATPT_VOLUME_CTL(kname, pname) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = kname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .info = catpt_volume_info, \ + .get = catpt_volume_get, \ + .put = catpt_volume_put, \ + .tlv.p = catpt_volume_tlv, \ + .private_value = (unsigned long) \ + &(struct catpt_control_data) { CATPT_PIN_ID_##pname } \ +} static const struct snd_kcontrol_new component_kcontrols[] = { /* Master volume (mixer stream) */ -CATPT_VOLUME_CTL("Master Playback Volume", mixer), +CATPT_VOLUME_CTL("Master Playback Volume", MIXER), /* Individual volume controls for offload and capture */ -CATPT_VOLUME_CTL("Media0 Playback Volume", offload1), -CATPT_VOLUME_CTL("Media1 Playback Volume", offload2), -CATPT_VOLUME_CTL("Mic Capture Volume", capture), +CATPT_VOLUME_CTL("Media0 Playback Volume", OFFLOAD1), +CATPT_VOLUME_CTL("Media1 Playback Volume", OFFLOAD2), +CATPT_VOLUME_CTL("Mic Capture Volume", CAPTURE1), SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0}, - catpt_loopback_switch_get, catpt_loopback_switch_put), + catpt_loopback_mute_get, catpt_loopback_mute_put), /* Enable or disable WAVES module */ SOC_SINGLE_BOOL_EXT("Waves Switch", 0, catpt_waves_switch_get, catpt_waves_switch_put), @@ -1122,7 +1056,7 @@ static const struct snd_soc_dapm_route component_routes[] = { static const struct snd_soc_component_driver catpt_comp_driver = { .name = "catpt-platform", - .pcm_construct = catpt_component_pcm_construct, + .pcm_new = catpt_component_pcm_new, .open = catpt_component_open, .pointer = catpt_component_pointer, 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 ddd919847c1f..3b7818355ff6 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -134,31 +134,6 @@ static const struct snd_soc_acpi_endpoint spk_6_endpoint = { .group_id = 1, }; -/* - * Multi-function codecs with three endpoints created for - * headset, amp and dmic functions. - */ -static const struct snd_soc_acpi_endpoint rt_mf_endpoints[] = { - { - .num = 0, - .aggregated = 0, - .group_position = 0, - .group_id = 0, - }, - { - .num = 1, - .aggregated = 0, - .group_position = 0, - .group_id = 0, - }, - { - .num = 2, - .aggregated = 0, - .group_position = 0, - .group_id = 0, - }, -}; - static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = { /* Jack Endpoint */ { @@ -365,33 +340,6 @@ static const struct snd_soc_acpi_adr_device rt722_0_agg_adr[] = { } }; -static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = { - { - .adr = 0x000030025d072201ull, - .num_endpoints = ARRAY_SIZE(rt_mf_endpoints), - .endpoints = rt_mf_endpoints, - .name_prefix = "rt722" - } -}; - -static const struct snd_soc_acpi_adr_device rt722_1_single_adr[] = { - { - .adr = 0x000130025d072201ull, - .num_endpoints = ARRAY_SIZE(rt_mf_endpoints), - .endpoints = rt_mf_endpoints, - .name_prefix = "rt722" - } -}; - -static const struct snd_soc_acpi_adr_device rt722_3_single_adr[] = { - { - .adr = 0x000330025d072201ull, - .num_endpoints = ARRAY_SIZE(rt_mf_endpoints), - .endpoints = rt_mf_endpoints, - .name_prefix = "rt722" - } -}; - static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = { { .adr = 0x000130025D132001ull, @@ -479,33 +427,6 @@ static const struct snd_soc_acpi_link_adr ptl_cs42l43_l2_cs35l56x6_l13[] = { {} }; -static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = { - { - .mask = BIT(0), - .num_adr = ARRAY_SIZE(rt722_0_single_adr), - .adr_d = rt722_0_single_adr, - }, - {} -}; - -static const struct snd_soc_acpi_link_adr ptl_rt722_l1[] = { - { - .mask = BIT(1), - .num_adr = ARRAY_SIZE(rt722_1_single_adr), - .adr_d = rt722_1_single_adr, - }, - {} -}; - -static const struct snd_soc_acpi_link_adr ptl_rt722_l3[] = { - { - .mask = BIT(3), - .num_adr = ARRAY_SIZE(rt722_3_single_adr), - .adr_d = rt722_3_single_adr, - }, - {} -}; - static const struct snd_soc_acpi_link_adr ptl_rt722_l0_rt1320_l23[] = { { .mask = BIT(0), @@ -699,20 +620,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .sof_tplg_filename = "sof-ptl-rt711.tplg", }, { - .link_mask = BIT(0), - .links = ptl_rt722_only, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-ptl-rt722.tplg", - .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(1), - .links = ptl_rt722_l1, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-ptl-rt722.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", @@ -720,13 +627,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg", .get_function_tplg_files = sof_sdw_get_tplg_files, }, - { - .link_mask = BIT(3), - .links = ptl_rt722_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-ptl-rt722.tplg", - .get_function_tplg_files = sof_sdw_get_tplg_files, - }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ptl_sdw_machines); diff --git a/sound/soc/intel/keembay/Makefile b/sound/soc/intel/keembay/Makefile index 3da9a6f9ba2a..fe3d761743d9 100644 --- a/sound/soc/intel/keembay/Makefile +++ b/sound/soc/intel/keembay/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-kmb_platform-y := \ kmb_platform.o diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index 4ed71d11ad77..6659e8060ef3 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -388,7 +388,7 @@ static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component, static const struct snd_soc_component_driver kmb_component = { .name = "kmb", - .pcm_construct = kmb_platform_pcm_new, + .pcm_new = kmb_platform_pcm_new, .open = kmb_pcm_open, .trigger = kmb_pcm_trigger, .pointer = kmb_pcm_pointer, diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index 036b42058272..bd09cb163e65 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -261,5 +261,5 @@ const struct snd_soc_component_driver kirkwood_soc_component = { .hw_params = kirkwood_dma_hw_params, .prepare = kirkwood_dma_prepare, .pointer = kirkwood_dma_pointer, - .pcm_construct = kirkwood_dma_new, + .pcm_new = kirkwood_dma_new, }; diff --git a/sound/soc/loongson/loongson_dma.c b/sound/soc/loongson/loongson_dma.c index f26b2951bc9c..a149b643175c 100644 --- a/sound/soc/loongson/loongson_dma.c +++ b/sound/soc/loongson/loongson_dma.c @@ -341,5 +341,5 @@ const struct snd_soc_component_driver loongson_i2s_component = { .trigger = loongson_pcm_trigger, .pointer = loongson_pcm_pointer, .mmap = loongson_pcm_mmap, - .pcm_construct = loongson_pcm_new, + .pcm_new = loongson_pcm_new, }; diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index 938dd3d46b00..f2b39fc9ec81 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -149,7 +149,7 @@ static int mtk_afe_component_probe(struct snd_soc_component *component) const struct snd_soc_component_driver mtk_afe_pcm_platform = { .name = AFE_PCM_NAME, .pointer = mtk_afe_pcm_pointer, - .pcm_construct = mtk_afe_pcm_new, + .pcm_new = mtk_afe_pcm_new, .probe = mtk_afe_component_probe, }; EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform); diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c index 085e993c650d..44a521c3a610 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c @@ -2773,7 +2773,7 @@ static int mt8186_afe_component_probe(struct snd_soc_component *component) static const struct snd_soc_component_driver mt8186_afe_component = { .name = AFE_PCM_NAME, - .pcm_construct = mtk_afe_pcm_new, + .pcm_new = mtk_afe_pcm_new, .pointer = mtk_afe_pcm_pointer, .probe = mt8186_afe_component_probe, }; diff --git a/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c index 166ece74270e..24b0c78815f6 100644 --- a/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c +++ b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c @@ -2378,8 +2378,8 @@ static void mt8189_afe_pcm_free(struct snd_soc_component *component, static const struct snd_soc_component_driver mt8189_afe_component = { .name = AFE_PCM_NAME, .probe = mt8189_afe_component_probe, - .pcm_construct = mtk_afe_pcm_new, - .pcm_destruct = mt8189_afe_pcm_free, + .pcm_new = mtk_afe_pcm_new, + .pcm_free = mt8189_afe_pcm_free, .open = mt8189_afe_pcm_open, .pointer = mtk_afe_pcm_pointer, }; diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 245f17411638..f1c0e612313d 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -157,13 +157,16 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n", ret); + mxs_saif_put_mclk(0); return ret; } } ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) + if (ret) { + mxs_saif_put_mclk(0); return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); + } return 0; } diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index c34bfa27a446..37bd8dbd541f 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -843,7 +843,7 @@ static struct snd_soc_dai_driver pxa_ssp_dai = { static const struct snd_soc_component_driver pxa_ssp_component = { .name = "pxa-ssp", - .pcm_construct = pxa2xx_soc_pcm_new, + .pcm_new = pxa2xx_soc_pcm_new, .open = pxa2xx_soc_pcm_open, .close = pxa2xx_soc_pcm_close, .hw_params = pxa2xx_soc_pcm_hw_params, diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 109a4958d9c0..a0c672602918 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -198,7 +198,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { static const struct snd_soc_component_driver pxa_ac97_component = { .name = "pxa-ac97", - .pcm_construct = pxa2xx_soc_pcm_new, + .pcm_new = pxa2xx_soc_pcm_new, .open = pxa2xx_soc_pcm_open, .close = pxa2xx_soc_pcm_close, .hw_params = pxa2xx_soc_pcm_hw_params, diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 849fbf176a70..f6ada6cffc88 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -356,7 +356,7 @@ static struct snd_soc_dai_driver pxa_i2s_dai = { static const struct snd_soc_component_driver pxa_i2s_component = { .name = "pxa-i2s", - .pcm_construct = pxa2xx_soc_pcm_new, + .pcm_new = pxa2xx_soc_pcm_new, .open = pxa2xx_soc_pcm_open, .close = pxa2xx_soc_pcm_close, .hw_params = pxa2xx_soc_pcm_hw_params, diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 9d6c41f775e5..ff0fbb61dccd 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -18,7 +18,7 @@ #include <sound/dmaengine_pcm.h> static const struct snd_soc_component_driver pxa2xx_soc_platform = { - .pcm_construct = pxa2xx_soc_pcm_new, + .pcm_new = pxa2xx_soc_pcm_new, .open = pxa2xx_soc_pcm_open, .close = pxa2xx_soc_pcm_close, .hw_params = pxa2xx_soc_pcm_hw_params, diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 7ee60a58a336..cf1f3a767cee 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -120,6 +120,12 @@ int qcom_snd_parse_of(struct snd_soc_card *card) link->id = args.args[0]; + if (link->id >= LPASS_MAX_PORT) { + dev_err(dev, "%s: Invalid cpu dai id %d\n", link->name, link->id); + ret = -EINVAL; + goto err; + } + if (platform) { link->platforms->of_node = of_parse_phandle(platform, "sound-dai", diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index 1b8d3f90bffa..ee6662885593 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -4,8 +4,11 @@ #ifndef __QCOM_SND_COMMON_H__ #define __QCOM_SND_COMMON_H__ +#include <dt-bindings/sound/qcom,q6afe.h> #include <sound/soc.h> +#define LPASS_MAX_PORT (SENARY_MI2S_TX + 1) + int qcom_snd_parse_of(struct snd_soc_card *card); int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_jack *jack, bool *jack_setup); diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index ce6896cc015d..e162627d6f86 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -1268,7 +1268,7 @@ static const struct snd_soc_component_driver lpass_component_driver = { .trigger = lpass_platform_pcmops_trigger, .pointer = lpass_platform_pcmops_pointer, .mmap = lpass_platform_pcmops_mmap, - .pcm_construct = lpass_platform_pcm_new, + .pcm_new = lpass_platform_pcm_new, .suspend = lpass_platform_pcmops_suspend, .resume = lpass_platform_pcmops_resume, .copy = lpass_platform_copy, diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index de3ec6f594c1..68b71039b981 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -14,10 +14,11 @@ #include <linux/regmap.h> #include <dt-bindings/sound/qcom,lpass.h> #include <dt-bindings/sound/qcom,q6afe.h> +#include "common.h" #include "lpass-hdmi.h" #define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 -#define LPASS_MAX_PORTS (DISPLAY_PORT_RX_7 + 1) +#define LPASS_MAX_PORTS (LPASS_MAX_PORT) #define LPASS_MAX_MI2S_PORTS (8) #define LPASS_MAX_DMA_CHANNELS (8) #define LPASS_MAX_HDMI_DMA_CHANNELS (4) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 241c3b4479c6..0eff5e572573 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -1365,9 +1365,14 @@ int audioreach_set_media_format(struct q6apm_graph *graph, case MODULE_ID_SPEAKER_PROTECTION: rc = audioreach_speaker_protection(graph, module, PARAM_ID_SP_OP_MODE_NORMAL); + if (!rc) + rc = audioreach_module_enable(graph, module, true); + break; case MODULE_ID_SPEAKER_PROTECTION_VI: rc = audioreach_speaker_protection_vi(graph, module, cfg); + if (!rc) + rc = audioreach_module_enable(graph, module, true); break; default: @@ -1396,66 +1401,6 @@ void audioreach_graph_free_buf(struct q6apm_graph *graph) } EXPORT_SYMBOL_GPL(audioreach_graph_free_buf); -int audioreach_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, size_t period_sz, - unsigned int periods, bool is_contiguous) -{ - struct apm_shared_map_region_payload *mregions; - struct apm_cmd_shared_mem_map_regions *cmd; - uint32_t num_regions, buf_sz, payload_size; - struct audioreach_graph_data *data; - struct gpr_pkt *pkt __free(kfree) = NULL; - void *p; - int i; - - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - data = &graph->rx_data; - else - data = &graph->tx_data; - - if (is_contiguous) { - num_regions = 1; - buf_sz = period_sz * periods; - } else { - buf_sz = period_sz; - num_regions = periods; - } - - /* DSP expects size should be aligned to 4K */ - buf_sz = ALIGN(buf_sz, 4096); - - payload_size = sizeof(*cmd) + (sizeof(*mregions) * num_regions); - - pkt = audioreach_alloc_apm_pkt(payload_size, APM_CMD_SHARED_MEM_MAP_REGIONS, dir, - graph->port->id); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - - p = (void *)pkt + GPR_HDR_SIZE; - cmd = p; - cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL; - cmd->num_regions = num_regions; - - cmd->property_flag = 0x0; - - mregions = p + sizeof(*cmd); - - mutex_lock(&graph->lock); - - for (i = 0; i < num_regions; i++) { - struct audio_buffer *ab; - - ab = &data->buf[i]; - mregions->shm_addr_lsw = lower_32_bits(ab->phys); - mregions->shm_addr_msw = upper_32_bits(ab->phys); - mregions->mem_size_bytes = buf_sz; - ++mregions; - } - mutex_unlock(&graph->lock); - - return audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS); -} -EXPORT_SYMBOL_GPL(audioreach_map_memory_regions); - int audioreach_shared_memory_send_eos(struct q6apm_graph *graph) { struct data_cmd_wr_sh_mem_ep_eos *eos; diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 89f172aab8c0..6ddc287f0fb4 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -722,6 +722,7 @@ struct audioreach_connection { struct audioreach_graph_info { int id; + uint32_t mem_map_handle; uint32_t num_sub_graphs; struct list_head sg_list; /* DPCM connection from FE Graph to BE graph */ @@ -838,10 +839,6 @@ int audioreach_tplg_init(struct snd_soc_component *component); /* Module specific */ void audioreach_graph_free_buf(struct q6apm_graph *graph); -int audioreach_map_memory_regions(struct q6apm_graph *graph, - unsigned int dir, size_t period_sz, - unsigned int periods, - bool is_contiguous); int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait, struct gpr_pkt *pkt, uint32_t rsp_opcode); diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 0f47aadaabe1..a0d21034a626 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -409,6 +409,7 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, q6afe_slim_port_prepare(dai_data->port[dai->id], &dai_data->port_config[dai->id].slim); break; + case SENARY_MI2S_RX ... SENARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: rc = q6afe_i2s_port_prepare(dai_data->port[dai->id], @@ -540,6 +541,7 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"Tertiary MI2S Playback", NULL, "TERT_MI2S_RX"}, {"Quaternary MI2S Playback", NULL, "QUAT_MI2S_RX"}, {"Quinary MI2S Playback", NULL, "QUIN_MI2S_RX"}, + {"Senary MI2S Playback", NULL, "SEN_MI2S_RX"}, {"Primary TDM0 Playback", NULL, "PRIMARY_TDM_RX_0"}, {"Primary TDM1 Playback", NULL, "PRIMARY_TDM_RX_1"}, @@ -636,6 +638,7 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"SEC_MI2S_TX", NULL, "Secondary MI2S Capture"}, {"QUAT_MI2S_TX", NULL, "Quaternary MI2S Capture"}, {"QUIN_MI2S_TX", NULL, "Quinary MI2S Capture"}, + {"SEN_MI2S_TX", NULL, "Senary MI2S Capture"}, {"WSA_CODEC_DMA_RX_0 Playback", NULL, "WSA_CODEC_DMA_RX_0"}, {"WSA_CODEC_DMA_TX_0", NULL, "WSA_CODEC_DMA_TX_0 Capture"}, @@ -770,6 +773,8 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_TX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_TX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_TX", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SEN_MI2S_RX", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEN_MI2S_TX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("QUIN_MI2S_RX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_TX", NULL, @@ -1037,6 +1042,7 @@ static void of_q6afe_parse_dai_data(struct device *dev, switch (id) { /* MI2S specific properties */ + case SENARY_MI2S_RX ... SENARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: priv = &data->priv[id]; diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 43d877322bae..40237267fda0 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -130,6 +130,8 @@ #define AFE_PORT_ID_QUATERNARY_MI2S_TX 0x1007 #define AFE_PORT_ID_QUINARY_MI2S_RX 0x1016 #define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 +#define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 +#define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 /* Start of the range of port IDs for TDM devices. */ #define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 @@ -718,6 +720,10 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { QUINARY_MI2S_RX, 1, 1}, [QUINARY_MI2S_TX] = { AFE_PORT_ID_QUINARY_MI2S_TX, QUINARY_MI2S_TX, 0, 1}, + [SENARY_MI2S_RX] = { AFE_PORT_ID_SENARY_MI2S_RX, + SENARY_MI2S_RX, 1, 1}, + [SENARY_MI2S_TX] = { AFE_PORT_ID_SENARY_MI2S_TX, + SENARY_MI2S_TX, 0, 1}, [PRIMARY_TDM_RX_0] = { AFE_PORT_ID_PRIMARY_TDM_RX, PRIMARY_TDM_RX_0, 1, 1}, [PRIMARY_TDM_TX_0] = { AFE_PORT_ID_PRIMARY_TDM_TX, @@ -1777,6 +1783,8 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_QUATERNARY_MI2S_TX: case AFE_PORT_ID_QUINARY_MI2S_RX: case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_RX: + case AFE_PORT_ID_SENARY_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/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h index a29abe4ce436..0b8c3ec1315c 100644 --- a/sound/soc/qcom/qdsp6/q6afe.h +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -2,8 +2,9 @@ #ifndef __Q6AFE_H__ #define __Q6AFE_H__ +#include "../common.h" -#define AFE_PORT_MAX 137 +#define AFE_PORT_MAX (LPASS_MAX_PORT) #define MSM_AFE_PORT_TYPE_RX 0 #define MSM_AFE_PORT_TYPE_TX 1 diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 168c166c960d..ede19fdea6e9 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -228,11 +228,10 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, cfg.bit_width = prtd->bits_per_sample; cfg.fmt = SND_AUDIOCODEC_PCM; audioreach_set_default_channel_mapping(cfg.channel_map, runtime->channels); - if (prtd->state) { /* clear the previous setup if any */ q6apm_graph_stop(prtd->graph); - q6apm_unmap_memory_regions(prtd->graph, substream->stream); + q6apm_free_fragments(prtd->graph, substream->stream); } prtd->pcm_count = snd_pcm_lib_period_bytes(substream); @@ -247,8 +246,8 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, if (ret < 0) dev_err(dev, "%s: CMD Format block failed\n", __func__); - ret = q6apm_map_memory_regions(prtd->graph, substream->stream, prtd->phys, - (prtd->pcm_size / prtd->periods), prtd->periods); + ret = q6apm_alloc_fragments(prtd->graph, substream->stream, prtd->phys, + (prtd->pcm_size / prtd->periods), prtd->periods); if (ret < 0) { dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); @@ -317,6 +316,7 @@ static int q6apm_dai_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_STOP: /* TODO support be handled via SoftPause Module */ prtd->state = Q6APM_STREAM_STOPPED; + prtd->queue_ptr = 0; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -354,7 +354,7 @@ static int q6apm_dai_open(struct snd_soc_component *component, spin_lock_init(&prtd->lock); prtd->substream = substream; - prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id); + prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id, substream->stream); if (IS_ERR(prtd->graph)) { dev_err(dev, "%s: Could not allocate memory\n", __func__); ret = PTR_ERR(prtd->graph); @@ -415,9 +415,10 @@ static int q6apm_dai_close(struct snd_soc_component *component, struct snd_pcm_runtime *runtime = substream->runtime; struct q6apm_dai_rtd *prtd = runtime->private_data; - if (prtd->state) { /* only stop graph that is started */ + if (prtd->state) { + /* only stop graph that is started */ q6apm_graph_stop(prtd->graph); - q6apm_unmap_memory_regions(prtd->graph, substream->stream); + q6apm_free_fragments(prtd->graph, substream->stream); } q6apm_graph_close(prtd->graph); @@ -466,11 +467,94 @@ static int q6apm_dai_hw_params(struct snd_soc_component *component, return 0; } +static int q6apm_dai_memory_map(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int graph_id) +{ + struct q6apm_dai_data *pdata; + struct device *dev = component->dev; + phys_addr_t phys; + int ret; + + pdata = snd_soc_component_get_drvdata(component); + if (!pdata) { + dev_err(component->dev, "Drv data not found ..\n"); + return -EINVAL; + } + + if (pdata->sid < 0) + phys = substream->dma_buffer.addr; + else + phys = substream->dma_buffer.addr | (pdata->sid << 32); + + ret = q6apm_map_memory_fixed_region(dev, graph_id, phys, BUFFER_BYTES_MAX); + if (ret < 0) + dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + + return ret; +} + static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_pcm *pcm = rtd->pcm; int size = BUFFER_BYTES_MAX; + int graph_id, ret; + struct snd_pcm_substream *substream; + + graph_id = cpu_dai->driver->id; + + ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); + if (ret) + return ret; + + /* Note: DSP backend dais are uni-directional ONLY(either playback or capture) */ + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + ret = q6apm_dai_memory_map(component, substream, graph_id); + if (ret) + return ret; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + ret = q6apm_dai_memory_map(component, substream, graph_id); + if (ret) + return ret; + } + + return 0; +} + +static void q6apm_dai_memory_unmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *soc_prtd; + struct snd_soc_dai *cpu_dai; + int graph_id; + + soc_prtd = snd_soc_substream_to_rtd(substream); + if (!soc_prtd) + return; + + cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0); + if (!cpu_dai) + return; + + graph_id = cpu_dai->driver->id; + q6apm_unmap_memory_fixed_region(component->dev, graph_id); +} + +static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (substream) + q6apm_dai_memory_unmap(component, substream); - return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (substream) + q6apm_dai_memory_unmap(component, substream); } static int q6apm_dai_compr_open(struct snd_soc_component *component, @@ -495,7 +579,8 @@ static int q6apm_dai_compr_open(struct snd_soc_component *component, return -ENOMEM; prtd->cstream = stream; - prtd->graph = q6apm_graph_open(dev, event_handler_compr, prtd, graph_id); + prtd->graph = q6apm_graph_open(dev, event_handler_compr, prtd, graph_id, + SNDRV_PCM_STREAM_PLAYBACK); if (IS_ERR(prtd->graph)) { ret = PTR_ERR(prtd->graph); kfree(prtd); @@ -528,7 +613,8 @@ static int q6apm_dai_compr_free(struct snd_soc_component *component, struct q6apm_dai_rtd *prtd = runtime->private_data; q6apm_graph_stop(prtd->graph); - q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK); + q6apm_free_fragments(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK); + q6apm_unmap_memory_fixed_region(component->dev, prtd->graph->id); q6apm_graph_close(prtd->graph); snd_dma_free_pages(&prtd->dma_buffer); prtd->graph = NULL; @@ -677,9 +763,9 @@ static int q6apm_dai_compr_set_params(struct snd_soc_component *component, if (ret) return ret; - ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK, - prtd->phys, (prtd->pcm_size / prtd->periods), - prtd->periods); + ret = q6apm_alloc_fragments(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK, + prtd->phys, (prtd->pcm_size / prtd->periods), + prtd->periods); if (ret < 0) return -ENOMEM; @@ -831,7 +917,8 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = { .open = q6apm_dai_open, .close = q6apm_dai_close, .prepare = q6apm_dai_prepare, - .pcm_construct = q6apm_dai_pcm_new, + .pcm_new = q6apm_dai_pcm_new, + .pcm_free = q6apm_dai_pcm_free, .hw_params = q6apm_dai_hw_params, .pointer = q6apm_dai_pointer, .trigger = q6apm_dai_trigger, diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329..006b283484d9 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -150,7 +150,7 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct rc = q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; if (rc < 0) - dev_err(dai->dev, "fail to close APM port (%d)\n", rc); + dev_err(dai->dev, "failed to stop APM port (%d)\n", rc); } if (dai_data->graph[dai->id]) { @@ -159,6 +159,31 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct } } +static int q6apm_lpass_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!dai_data->is_port_started[dai->id]) { + ret = q6apm_graph_start(dai_data->graph[dai->id]); + if (ret < 0) + dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); + else + dai_data->is_port_started[dai->id] = true; + } + break; + default: + break; + } + + return ret; +} + static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -171,18 +196,14 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - q6apm_graph_close(dai_data->graph[dai->id]); - dai_data->graph[dai->id] = NULL; - } } /** * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); rc = PTR_ERR(graph); @@ -203,14 +224,6 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s dev_err(dai->dev, "Failed to prepare Graph %d\n", rc); goto err; } - - rc = q6apm_graph_start(dai_data->graph[dai->id]); - if (rc < 0) { - dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); - goto err; - } - dai_data->is_port_started[dai->id] = true; - return 0; err: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -227,7 +240,7 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s int graph_id = dai->id; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); return PTR_ERR(graph); @@ -254,6 +267,7 @@ static const struct snd_soc_dai_ops q6dma_ops = { .shutdown = q6apm_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6i2s_ops = { @@ -263,6 +277,7 @@ static const struct snd_soc_dai_ops q6i2s_ops = { .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6hdmi_ops = { @@ -271,6 +286,7 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .shutdown = q6apm_lpass_dai_shutdown, .hw_params = q6hdmi_hw_params, .set_fmt = q6i2s_set_fmt, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_component_driver q6apm_lpass_dai_component = { diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 970b08c89bb3..3c119a6132e4 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -200,13 +200,53 @@ int q6apm_graph_media_format_shmem(struct q6apm_graph *graph, } EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem); -int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys, - size_t period_sz, unsigned int periods) +int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id, phys_addr_t phys, + size_t sz) +{ + struct audioreach_graph_info *info; + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct apm_shared_map_region_payload *mregions; + struct apm_cmd_shared_mem_map_regions *cmd; + int payload_size = sizeof(*cmd) + (sizeof(*mregions)); + uint32_t buf_sz; + void *p; + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, + APM_CMD_SHARED_MEM_MAP_REGIONS, graph_id); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + info = idr_find(&apm->graph_info_idr, graph_id); + if (!info) + return -ENODEV; + + if (info->mem_map_handle) + return 0; + + /* DSP expects size should be aligned to 4K */ + buf_sz = ALIGN(sz, 4096); + + p = (void *)pkt + GPR_HDR_SIZE; + cmd = p; + cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL; + cmd->num_regions = 1; + cmd->property_flag = 0x0; + + mregions = p + sizeof(*cmd); + + mregions->shm_addr_lsw = lower_32_bits(phys); + mregions->shm_addr_msw = upper_32_bits(phys); + mregions->mem_size_bytes = buf_sz; + + return q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS); +} +EXPORT_SYMBOL_GPL(q6apm_map_memory_fixed_region); + +int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys, + size_t period_sz, unsigned int periods) { struct audioreach_graph_data *data; struct audio_buffer *buf; int cnt; - int rc; if (dir == SNDRV_PCM_STREAM_PLAYBACK) data = &graph->rx_data; @@ -215,6 +255,8 @@ int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_a mutex_lock(&graph->lock); + data->dsp_buf = 0; + if (data->buf) { mutex_unlock(&graph->lock); return 0; @@ -246,46 +288,41 @@ int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_a mutex_unlock(&graph->lock); - rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1); - if (rc < 0) { - dev_err(graph->dev, "Memory_map_regions failed\n"); - audioreach_graph_free_buf(graph); - } - - return rc; + return 0; } -EXPORT_SYMBOL_GPL(q6apm_map_memory_regions); +EXPORT_SYMBOL_GPL(q6apm_alloc_fragments); -int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) +int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id) { struct apm_cmd_shared_mem_unmap_regions *cmd; - struct audioreach_graph_data *data; - int rc; + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_graph_info *info; + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), + APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - data = &graph->rx_data; - else - data = &graph->tx_data; + info = idr_find(&apm->graph_info_idr, graph_id); + if (!info) + return -ENODEV; - if (!data->mem_map_handle) + if (!info->mem_map_handle) return 0; - struct gpr_pkt *pkt __free(kfree) = - audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, - dir, graph->port->id); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - cmd = (void *)pkt + GPR_HDR_SIZE; - cmd->mem_map_handle = data->mem_map_handle; + cmd->mem_map_handle = info->mem_map_handle; - rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS); + return q6apm_send_cmd_sync(apm, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS); +} +EXPORT_SYMBOL_GPL(q6apm_unmap_memory_fixed_region); +int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir) +{ audioreach_graph_free_buf(graph); - return rc; + return 0; } -EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions); +EXPORT_SYMBOL_GPL(q6apm_free_fragments); int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) { @@ -409,12 +446,11 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, { struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer; struct audio_buffer *ab; - int iid = q6apm_graph_get_rx_shmem_module_iid(graph); struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer), DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT), - graph->port->id, iid); + graph->port->id, graph->shm_iid); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -428,7 +464,7 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, write_buffer->buf_size = len; write_buffer->timestamp_lsw = lsw_ts; write_buffer->timestamp_msw = msw_ts; - write_buffer->mem_map_handle = graph->rx_data.mem_map_handle; + write_buffer->mem_map_handle = graph->info->mem_map_handle; write_buffer->flags = wflags; graph->rx_data.dsp_buf++; @@ -447,11 +483,10 @@ int q6apm_read(struct q6apm_graph *graph) struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer; struct audioreach_graph_data *port; struct audio_buffer *ab; - int iid = q6apm_graph_get_tx_shmem_module_iid(graph); struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer), DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2, - graph->tx_data.dsp_buf, graph->port->id, iid); + graph->tx_data.dsp_buf, graph->port->id, graph->shm_iid); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -463,7 +498,7 @@ int q6apm_read(struct q6apm_graph *graph) read_buffer->buf_addr_lsw = lower_32_bits(ab->phys); read_buffer->buf_addr_msw = upper_32_bits(ab->phys); - read_buffer->mem_map_handle = port->mem_map_handle; + read_buffer->mem_map_handle = graph->info->mem_map_handle; read_buffer->buf_size = ab->size; port->dsp_buf++; @@ -494,7 +529,6 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) { struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done; struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done; - struct apm_cmd_rsp_shared_mem_map_regions *rsp; const struct gpr_ibasic_rsp_result_t *result; struct q6apm_graph *graph = priv; const struct gpr_hdr *hdr = &data->hdr; @@ -530,18 +564,6 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) } break; - case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS: - graph->result.opcode = hdr->opcode; - graph->result.status = 0; - rsp = data->payload; - - if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK) - graph->rx_data.mem_map_handle = rsp->mem_map_handle; - else - graph->tx_data.mem_map_handle = rsp->mem_map_handle; - - wake_up(&graph->cmd_wait); - break; case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2: if (!graph->ar_graph) break; @@ -571,16 +593,6 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) { - case APM_CMD_SHARED_MEM_UNMAP_REGIONS: - graph->result.opcode = result->opcode; - graph->result.status = 0; - if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK) - graph->rx_data.mem_map_handle = 0; - else - graph->tx_data.mem_map_handle = 0; - - wake_up(&graph->cmd_wait); - break; case APM_CMD_SHARED_MEM_MAP_REGIONS: case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT: case APM_CMD_SET_CFG: @@ -602,7 +614,7 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) } struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, - void *priv, int graph_id) + void *priv, int graph_id, int dir) { struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph *ar_graph; @@ -629,6 +641,12 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, graph->id = ar_graph->id; graph->dev = dev; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + graph->shm_iid = q6apm_graph_get_rx_shmem_module_iid(graph); + else + graph->shm_iid = q6apm_graph_get_tx_shmem_module_iid(graph); + + mutex_init(&graph->lock); init_waitqueue_head(&graph->cmd_wait); @@ -747,13 +765,23 @@ static int apm_probe(gpr_device_t *gdev) q6apm_get_apm_state(apm); - ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0); + ret = snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0); if (ret < 0) { dev_err(dev, "failed to register q6apm: %d\n", ret); return ret; } - return of_platform_populate(dev->of_node, NULL, NULL, dev); + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret) + snd_soc_unregister_component(dev); + + return ret; +} + +static void apm_remove(gpr_device_t *gdev) +{ + of_platform_depopulate(&gdev->dev); + snd_soc_unregister_component(&gdev->dev); } struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid) @@ -768,7 +796,9 @@ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, ui static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) { gpr_device_t *gdev = priv; + struct audioreach_graph_info *info; struct q6apm *apm = dev_get_drvdata(&gdev->dev); + struct apm_cmd_rsp_shared_mem_map_regions *rsp; struct device *dev = &gdev->dev; struct gpr_ibasic_rsp_result_t *result; const struct gpr_hdr *hdr = &data->hdr; @@ -785,6 +815,7 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) { + case APM_CMD_SHARED_MEM_MAP_REGIONS: case APM_CMD_GRAPH_START: case APM_CMD_GRAPH_OPEN: case APM_CMD_GRAPH_PREPARE: @@ -799,10 +830,38 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) result->opcode); wake_up(&apm->wait); break; + case APM_CMD_SHARED_MEM_UNMAP_REGIONS: + apm->result.opcode = hdr->opcode; + apm->result.status = 0; + rsp = data->payload; + + info = idr_find(&apm->graph_info_idr, hdr->token); + if (info) + info->mem_map_handle = 0; + else + dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, + result->opcode); + + wake_up(&apm->wait); + break; default: break; } break; + case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS: + apm->result.opcode = hdr->opcode; + apm->result.status = 0; + rsp = data->payload; + + info = idr_find(&apm->graph_info_idr, hdr->token); + if (info) + info->mem_map_handle = rsp->mem_map_handle; + else + dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, + result->opcode); + + wake_up(&apm->wait); + break; default: break; } @@ -820,6 +879,7 @@ MODULE_DEVICE_TABLE(of, apm_device_id); static gpr_driver_t apm_driver = { .probe = apm_probe, + .remove = apm_remove, .gpr_callback = apm_callback, .driver = { .name = "qcom-apm", diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 7ce08b401e31..909fc337fd28 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -14,9 +14,10 @@ #include <linux/of_platform.h> #include <linux/jiffies.h> #include <linux/soc/qcom/apr.h> +#include "../common.h" #include "audioreach.h" -#define APM_PORT_MAX 127 +#define APM_PORT_MAX LPASS_MAX_PORT #define APM_PORT_MAX_AUDIO_CHAN_CNT 8 #define PCM_CHANNEL_NULL 0 #define PCM_CHANNEL_FL 1 /* Front left channel. */ @@ -77,7 +78,6 @@ struct audioreach_graph_data { struct audio_buffer *buf; uint32_t num_periods; uint32_t dsp_buf; - uint32_t mem_map_handle; atomic_t hw_ptr; }; @@ -98,6 +98,7 @@ struct q6apm_graph { void *priv; q6apm_cb cb; uint32_t id; + uint32_t shm_iid; struct device *dev; struct q6apm *apm; gpr_port_t *port; @@ -112,7 +113,7 @@ struct q6apm_graph { /* Graph Operations */ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, - void *priv, int graph_id); + void *priv, int graph_id, int dir); int q6apm_graph_close(struct q6apm_graph *graph); int q6apm_graph_prepare(struct q6apm_graph *graph); int q6apm_graph_start(struct q6apm_graph *graph); @@ -132,11 +133,14 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags); /* Memory Map related */ -int q6apm_map_memory_regions(struct q6apm_graph *graph, - unsigned int dir, phys_addr_t phys, - size_t period_sz, unsigned int periods); -int q6apm_unmap_memory_regions(struct q6apm_graph *graph, - unsigned int dir); +int q6apm_map_memory_fixed_region(struct device *dev, + unsigned int graph_id, phys_addr_t phys, + size_t sz); +int q6apm_alloc_fragments(struct q6apm_graph *graph, + unsigned int dir, phys_addr_t phys, + size_t period_sz, unsigned int periods); +int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir); +int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id); /* Helpers */ int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode); diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 9e3d176f50c2..4f8f7db6c3d3 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -1224,7 +1224,7 @@ static const struct snd_soc_component_driver q6asm_fe_dai_component = { .trigger = q6asm_dai_trigger, .ack = q6asm_dai_ack, .pointer = q6asm_dai_pointer, - .pcm_construct = q6asm_dai_pcm_new, + .pcm_new = q6asm_dai_pcm_new, .compress_ops = &q6asm_dai_compress_ops, .dapm_widgets = q6asm_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(q6asm_dapm_widgets), diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index 4eed54b071a5..e5cd82f77b55 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -96,6 +96,42 @@ .id = did, \ } +#define Q6AFE_MI2S_RX_DAI(pre, did) { \ + .playback = { \ + .stream_name = pre" MI2S Playback", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .id = did, \ + } + +#define Q6AFE_MI2S_TX_DAI(pre, did) { \ + .capture = { \ + .stream_name = pre" MI2S Capture", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .id = did, \ + } + static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { { .playback = { @@ -483,7 +519,45 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { }, .id = QUINARY_MI2S_TX, .name = "QUIN_MI2S_TX", + }, { + .playback = { + .stream_name = "Senary MI2S Playback", + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .id = SENARY_MI2S_RX, + .name = "SEN_MI2S_RX", + }, { + .capture = { + .stream_name = "Senary MI2S Capture", + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .id = SENARY_MI2S_TX, + .name = "SEN_MI2S_TX", }, + Q6AFE_MI2S_RX_DAI("LPI RX0", LPI_MI2S_RX_0), + Q6AFE_MI2S_RX_DAI("LPI RX1", LPI_MI2S_RX_1), + 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_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_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), @@ -634,8 +708,10 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, case SLIMBUS_0_RX ... SLIMBUS_6_TX: q6dsp_audio_fe_dais[i].ops = cfg->q6slim_ops; break; + 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: 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/topology.c b/sound/soc/qcom/qdsp6/topology.c index e732fac9b8ca..1f69fba6de26 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -952,9 +952,6 @@ static int audioreach_widget_unload(struct snd_soc_component *scomp, struct audioreach_container *cont; struct audioreach_module *mod; - mod = dobj->private; - cont = mod->container; - if (w->id == snd_soc_dapm_mixer) { /* virtual widget */ struct snd_ar_control *scontrol = dobj->private; @@ -963,6 +960,11 @@ static int audioreach_widget_unload(struct snd_soc_component *scomp, kfree(scontrol); return 0; } + mod = dobj->private; + if (!mod) + return 0; + + cont = mod->container; mutex_lock(&apm->lock); idr_remove(&apm->modules_idr, mod->instance_id); diff --git a/sound/soc/renesas/dma-sh7760.c b/sound/soc/renesas/dma-sh7760.c index c53539482c20..810f44cd6523 100644 --- a/sound/soc/renesas/dma-sh7760.c +++ b/sound/soc/renesas/dma-sh7760.c @@ -58,8 +58,9 @@ struct camelot_pcm { struct snd_pcm_substream *rx_ss; unsigned long rx_period_size; unsigned int rx_period; +}; -} cam_pcm_data[2] = { +static struct camelot_pcm cam_pcm_data[2] = { { .mmio = 0xFE3C0040, .txid = DMABRGIRQ_A0TXF, @@ -310,7 +311,7 @@ static const struct snd_soc_component_driver sh7760_soc_component = { .prepare = camelot_prepare, .trigger = camelot_trigger, .pointer = camelot_pos, - .pcm_construct = camelot_pcm_new, + .pcm_new = camelot_pcm_new, }; static int sh7760_soc_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c index 1491c2f2cc96..8cbd7acc26f4 100644 --- a/sound/soc/renesas/fsi.c +++ b/sound/soc/renesas/fsi.c @@ -1817,7 +1817,7 @@ static const struct snd_soc_component_driver fsi_soc_component = { .name = "fsi", .open = fsi_pcm_open, .pointer = fsi_pointer, - .pcm_construct = fsi_pcm_new, + .pcm_new = fsi_pcm_new, }; /* diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index 69fb19964a71..2dc078358612 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -1974,7 +1974,7 @@ static int rsnd_probe(struct platform_device *pdev) * asoc register */ ci = 0; - for (i = 0; priv->component_dais[i] > 0; i++) { + for (i = 0; i < RSND_MAX_COMPONENT && priv->component_dais[i] > 0; i++) { int nr = priv->component_dais[i]; ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c index f2addfbac923..2671abc028cc 100644 --- a/sound/soc/renesas/rcar/msiof.c +++ b/sound/soc/renesas/rcar/msiof.c @@ -514,7 +514,7 @@ static const struct snd_soc_component_driver msiof_component_driver = { .open = msiof_open, .close = msiof_close, .pointer = msiof_pointer, - .pcm_construct = msiof_new, + .pcm_new = msiof_new, .trigger = msiof_trigger, .hw_params = msiof_hw_params, }; diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 74e078c04150..71e434cfe07b 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -1117,7 +1117,7 @@ static const struct snd_soc_component_driver rz_ssi_soc_component = { .name = "rz-ssi", .open = rz_ssi_pcm_open, .pointer = rz_ssi_pcm_pointer, - .pcm_construct = rz_ssi_pcm_new, + .pcm_new = rz_ssi_pcm_new, .legacy_dai_naming = 1, }; diff --git a/sound/soc/renesas/siu_pcm.c b/sound/soc/renesas/siu_pcm.c index f15ff36e7934..fd5b20b469a4 100644 --- a/sound/soc/renesas/siu_pcm.c +++ b/sound/soc/renesas/siu_pcm.c @@ -483,7 +483,6 @@ siu_pcm_pointer_dma(struct snd_soc_component *component, static int siu_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - /* card->dev == socdev->dev, see snd_soc_new_pcms() */ struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; struct siu_info *info = siu_i2s_data; @@ -546,8 +545,8 @@ const struct snd_soc_component_driver siu_component = { .prepare = siu_pcm_prepare, .trigger = siu_pcm_trigger, .pointer = siu_pcm_pointer_dma, - .pcm_construct = siu_pcm_new, - .pcm_destruct = siu_pcm_free, + .pcm_new = siu_pcm_new, + .pcm_free = siu_pcm_free, .legacy_dai_naming = 1, }; EXPORT_SYMBOL_GPL(siu_component); diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 1bf614dbdf4d..ed393e5034a4 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -628,6 +628,10 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val); + if (!sai->is_tdm) + regmap_update_bits(sai->regmap, reg, SAI_XCR_SBW_MASK, + SAI_XCR_SBW(params_physical_width(params))); + regmap_read(sai->regmap, reg, &val); slot_width = SAI_XCR_SBW_V(val); diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index beb7e09e9fac..cb455ddce253 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -399,8 +399,8 @@ static const struct snd_soc_component_driver asoc_idma_platform = { .hw_params = idma_hw_params, .hw_free = idma_hw_free, .prepare = idma_prepare, - .pcm_construct = idma_new, - .pcm_destruct = idma_free, + .pcm_new = idma_new, + .pcm_free = idma_free, }; static int asoc_idma_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 235d0063d1b3..fb30f6b637a0 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -407,21 +407,12 @@ static int spdif_probe(struct platform_device *pdev) if (ret) goto err1; - /* Request S/PDIF Register's memory region */ - if (!request_mem_region(mem_res->start, - resource_size(mem_res), "samsung-spdif")) { - dev_err(&pdev->dev, "Unable to request register region\n"); - ret = -EBUSY; + spdif->regs = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(spdif->regs)) { + ret = PTR_ERR(spdif->regs); goto err2; } - spdif->regs = ioremap(mem_res->start, 0x100); - if (spdif->regs == NULL) { - dev_err(&pdev->dev, "Cannot ioremap registers\n"); - ret = -ENXIO; - goto err3; - } - spdif_stereo_out.addr_width = 2; spdif_stereo_out.addr = mem_res->start + DATA_OUTBUF; filter = NULL; @@ -435,7 +426,7 @@ static int spdif_probe(struct platform_device *pdev) NULL, NULL, NULL); if (ret) { dev_err(&pdev->dev, "failed to register DMA: %d\n", ret); - goto err4; + goto err2; } dev_set_drvdata(&pdev->dev, spdif); @@ -444,14 +435,10 @@ static int spdif_probe(struct platform_device *pdev) &samsung_spdif_component, &samsung_spdif_dai, 1); if (ret != 0) { dev_err(&pdev->dev, "fail to register dai\n"); - goto err4; + goto err2; } return 0; -err4: - iounmap(spdif->regs); -err3: - release_mem_region(mem_res->start, resource_size(mem_res)); err2: clk_disable_unprepare(spdif->sclk); err1: @@ -463,12 +450,6 @@ err0: static void spdif_remove(struct platform_device *pdev) { struct samsung_spdif_info *spdif = &spdif_info; - struct resource *mem_res; - - iounmap(spdif->regs); - - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem_res->start, resource_size(mem_res)); clk_disable_unprepare(spdif->sclk); clk_disable_unprepare(spdif->pclk); diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index a0191e5a5a7d..2bfc8e5aee31 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -51,6 +51,25 @@ static bool readonly_control(struct sdca_control *control) return control->has_fixed || control->mode == SDCA_ACCESS_MODE_RO; } +static int ge_count_routes(struct sdca_entity *entity) +{ + int count = 0; + int i, j; + + for (i = 0; i < entity->ge.num_modes; i++) { + struct sdca_ge_mode *mode = &entity->ge.modes[i]; + + for (j = 0; j < mode->num_controls; j++) { + struct sdca_ge_control *affected = &mode->controls[j]; + + if (affected->sel != SDCA_CTL_SU_SELECTOR || affected->val) + count++; + } + } + + return count; +} + /** * sdca_asoc_count_component - count the various component parts * @dev: Pointer to the device against which allocations will be done. @@ -74,6 +93,7 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun int *num_widgets, int *num_routes, int *num_controls, int *num_dais) { + struct sdca_control *control; int i, j; *num_widgets = function->num_entities - 1; @@ -83,6 +103,7 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun for (i = 0; i < function->num_entities - 1; i++) { struct sdca_entity *entity = &function->entities[i]; + bool skip_primary_routes = false; /* Add supply/DAI widget connections */ switch (entity->type) { @@ -96,6 +117,17 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun case SDCA_ENTITY_TYPE_PDE: *num_routes += entity->pde.num_managed; break; + case SDCA_ENTITY_TYPE_GE: + *num_routes += ge_count_routes(entity); + skip_primary_routes = true; + break; + case SDCA_ENTITY_TYPE_SU: + control = sdca_selector_find_control(dev, entity, SDCA_CTL_SU_SELECTOR); + if (!control) + return -EINVAL; + + skip_primary_routes = (control->layers == SDCA_ACCESS_LAYER_DEVICE); + break; default: break; } @@ -104,7 +136,8 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun (*num_routes)++; /* Add primary entity connections from DisCo */ - *num_routes += entity->num_sources; + if (!skip_primary_routes) + *num_routes += entity->num_sources; for (j = 0; j < entity->num_controls; j++) { if (exported_control(entity, &entity->controls[j])) @@ -442,7 +475,6 @@ static int entity_parse_su_device(struct device *dev, struct snd_soc_dapm_route **route) { struct sdca_control_range *range; - int num_routes = 0; int i, j; if (!entity->group) { @@ -455,7 +487,7 @@ static int entity_parse_su_device(struct device *dev, if (!range) return -EINVAL; - (*widget)->id = snd_soc_dapm_mux; + (*widget)->id = snd_soc_dapm_mux_named_ctl; (*widget)->kcontrol_news = entity->group->ge.kctl; (*widget)->num_kcontrols = 1; (*widget)++; @@ -478,11 +510,6 @@ static int entity_parse_su_device(struct device *dev, return -EINVAL; } - if (++num_routes > entity->num_sources) { - dev_err(dev, "%s: too many input routes\n", entity->label); - return -EINVAL; - } - term = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX, mode->val, SDCA_SELECTED_MODE_TERM_TYPE); if (!term) { @@ -778,6 +805,72 @@ int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data *funct } EXPORT_SYMBOL_NS(sdca_asoc_populate_dapm, "SND_SOC_SDCA"); +static int q78_write(struct snd_soc_component *component, + struct soc_mixer_control *mc, + unsigned int reg, const int val) +{ + unsigned int mask = GENMASK(mc->sign_bit, 0); + unsigned int reg_val; + + if (val < 0 || val > mc->max - mc->min) + return -EINVAL; + + reg_val = (val + mc->min) * mc->shift; + + return snd_soc_component_update_bits(component, reg, mask, reg_val); +} + +int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret; + + ret = q78_write(component, mc, mc->reg, ucontrol->value.integer.value[0]); + if (ret < 0) + return ret; + + if (snd_soc_volsw_is_stereo(mc)) { + int err; /* Don't drop change flag */ + + err = q78_write(component, mc, mc->rreg, ucontrol->value.integer.value[1]); + if (err) + return err; + } + + return ret; +} +EXPORT_SYMBOL_NS(sdca_asoc_q78_put_volsw, "SND_SOC_SDCA"); + +static int q78_read(struct snd_soc_component *component, + struct soc_mixer_control *mc, unsigned int reg) +{ + unsigned int reg_val; + int val; + + reg_val = snd_soc_component_read(component, reg); + + val = (sign_extend32(reg_val, mc->sign_bit) / (int)mc->shift) - mc->min; + + return val & GENMASK(mc->sign_bit, 0); +} + +int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = q78_read(component, mc, mc->reg); + + if (snd_soc_volsw_is_stereo(mc)) + ucontrol->value.integer.value[1] = q78_read(component, mc, mc->rreg); + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_q78_get_volsw, "SND_SOC_SDCA"); + static int control_limit_kctl(struct device *dev, struct sdca_entity *entity, struct sdca_control *control, @@ -814,16 +907,15 @@ static int control_limit_kctl(struct device *dev, tlv[2] = (min * 100) >> 8; tlv[3] = (max * 100) >> 8; - step = (step * 100) >> 8; - - mc->min = ((int)tlv[2] / step); - mc->max = ((int)tlv[3] / step); + mc->min = min / step; + mc->max = max / step; mc->shift = step; mc->sign_bit = 15; - mc->sdca_q78 = 1; kctl->tlv.p = tlv; kctl->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + kctl->get = sdca_asoc_q78_get_volsw; + kctl->put = sdca_asoc_q78_put_volsw; return 0; } diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index 918b638acb57..6e9b66f71801 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -137,6 +137,13 @@ static const struct regmap_config class_dev_regmap_config = { .unlock = class_regmap_unlock, }; +static void class_remove_functions(void *data) +{ + struct sdca_class_drv *drv = data; + + sdca_dev_unregister_functions(drv->sdw); +} + static void class_boot_work(struct work_struct *work) { struct sdca_class_drv *drv = container_of(work, @@ -157,6 +164,11 @@ static void class_boot_work(struct work_struct *work) if (ret) goto err; + /* Ensure function drivers are removed before the IRQ is destroyed */ + ret = devm_add_action_or_reset(drv->dev, class_remove_functions, drv); + if (ret) + goto err; + dev_dbg(drv->dev, "boot work complete\n"); pm_runtime_mark_last_busy(drv->dev); @@ -168,15 +180,6 @@ err: pm_runtime_put_sync(drv->dev); } -static void class_dev_remove(void *data) -{ - struct sdca_class_drv *drv = data; - - cancel_work_sync(&drv->boot_work); - - sdca_dev_unregister_functions(drv->sdw); -} - static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id) { struct device *dev = &sdw->dev; @@ -230,15 +233,19 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id if (ret) return ret; - ret = devm_add_action_or_reset(dev, class_dev_remove, drv); - if (ret) - return ret; - queue_work(system_long_wq, &drv->boot_work); return 0; } +static void class_sdw_remove(struct sdw_slave *sdw) +{ + struct device *dev = &sdw->dev; + struct sdca_class_drv *drv = dev_get_drvdata(dev); + + cancel_work_sync(&drv->boot_work); +} + static int class_suspend(struct device *dev) { struct sdca_class_drv *drv = dev_get_drvdata(dev); @@ -317,6 +324,8 @@ static const struct dev_pm_ops class_pm_ops = { static const struct sdw_device_id class_sdw_id[] = { SDW_SLAVE_ENTRY(0x01FA, 0x4245, 0), + SDW_SLAVE_ENTRY(0x01FA, 0x4249, 0), + SDW_SLAVE_ENTRY(0x01FA, 0x4747, 0), {} }; MODULE_DEVICE_TABLE(sdw, class_sdw_id); @@ -328,6 +337,7 @@ static struct sdw_driver class_sdw_driver = { }, .probe = class_sdw_probe, + .remove = class_sdw_remove, .id_table = class_sdw_id, .ops = &class_sdw_ops, }; diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 730103120514..31fc08d51307 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -377,8 +377,14 @@ static int class_function_probe(struct auxiliary_device *auxdev, return dev_err_probe(dev, PTR_ERR(drv->regmap), "failed to create regmap"); - if (desc->type == SDCA_FUNCTION_TYPE_UAJ) + switch (desc->type) { + case SDCA_FUNCTION_TYPE_UAJ: + case SDCA_FUNCTION_TYPE_RJ: cmp_drv->set_jack = class_function_set_jack; + break; + default: + break; + } ret = sdca_asoc_populate_component(dev, drv->function, cmp_drv, &dais, &num_dais, @@ -555,6 +561,10 @@ static const struct auxiliary_device_id class_function_id_table[] = { .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_HID_NAME, .driver_data = SDCA_FUNCTION_TYPE_HID, }, + { + .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_RJ_NAME, + .driver_data = SDCA_FUNCTION_TYPE_RJ, + }, {}, }; MODULE_DEVICE_TABLE(auxiliary, class_function_id_table); diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index 07892bc3a44e..994821a6df61 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -46,11 +46,6 @@ int sdca_reset_function(struct device *dev, struct sdca_function_data *function, if (ret) // Allowed for function reset to not be implemented return 0; - if (!function->reset_max_delay) { - dev_err(dev, "No reset delay specified in DisCo\n"); - return -EINVAL; - } - /* * Poll up to 16 times but no more than once per ms, these are just * arbitrarily selected values, so may be fine tuned in future. diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index dca60ee8e62c..fd6a254c9530 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -2176,8 +2176,12 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, ret = fwnode_property_read_u32(function_desc->node, "mipi-sdca-function-reset-max-delay", &tmp); - if (!ret) + if (ret || tmp == 0) { + dev_dbg(dev, "reset delay missing, defaulting to 100mS\n"); + function->reset_max_delay = 100000; + } else { function->reset_max_delay = tmp; + } dev_dbg(dev, "%pfwP: name %s busy delay %dus reset delay %dus\n", function->desc->node, function->desc->name, diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 5cdabf8ae9da..6e10b4e660d9 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -119,7 +119,17 @@ static irqreturn_t function_status_handler(int irq, void *data) for_each_set_bit(mask, &status, BITS_PER_BYTE) { switch (BIT(mask)) { case SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION: - //FIXME: Add init writes +/* + * FIXME: Should this do init writes? + * + * Currently init writes/cache sync are done from the suspend/resume + * infrastructure. It is unclear in what situations one would receive this + * IRQ outside of that flow. Presumably it would be something like the chip + * crashing. In that case however doing the init writes and a cache sync might + * not be sufficient, for example if the failure was during audio playback + * there could be ordering constraints on the register writes to restore the + * state that are not handled by a simple cache sync. + */ break; case SDCA_CTL_ENTITY_0_FUNCTION_FAULT: dev_err(dev, "function fault\n"); @@ -555,7 +565,7 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA"); /** * sdca_irq_cleanup - Free all the individual IRQs for an SDCA Function - * @sdev: Device pointer against which the sdca_interrupt_info was allocated. + * @dev: Device pointer against which the sdca_interrupt_info was allocated. * @function: Pointer to the SDCA Function. * @info: Pointer to the SDCA interrupt info for this device. * @@ -630,13 +640,12 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA"); static void irq_enable_flags(struct sdca_function_data *function, struct sdca_interrupt_info *info, bool early) { - struct sdca_interrupt *interrupt; int i; for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { - interrupt = &info->irqs[i]; + struct sdca_interrupt *interrupt = &info->irqs[i]; - if (!interrupt || interrupt->function != function) + if (!interrupt->irq || interrupt->function != function) continue; switch (SDCA_CTL_TYPE(interrupt->entity->type, @@ -689,13 +698,12 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_enable, "SND_SOC_SDCA"); void sdca_irq_disable(struct sdca_function_data *function, struct sdca_interrupt_info *info) { - struct sdca_interrupt *interrupt; int i; for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { - interrupt = &info->irqs[i]; + struct sdca_interrupt *interrupt = &info->irqs[i]; - if (!interrupt || interrupt->function != function) + if (!interrupt->irq || interrupt->function != function) continue; disable_irq(interrupt->irq); diff --git a/sound/soc/sdw_utils/Makefile b/sound/soc/sdw_utils/Makefile index e8bd5ffb1a6a..a8d091fd374b 100644 --- a/sound/soc/sdw_utils/Makefile +++ b/sound/soc/sdw_utils/Makefile @@ -6,6 +6,7 @@ snd-soc-sdw-utils-y := soc_sdw_utils.o soc_sdw_dmic.o soc_sdw_rt_dmic.o \ soc_sdw_bridge_cs35l56.o \ soc_sdw_cs42l42.o soc_sdw_cs42l43.o \ soc_sdw_cs42l45.o \ + soc_sdw_cs47l47.o \ soc_sdw_cs_amp.o \ soc_sdw_maxim.o \ soc_sdw_ti_amp.o diff --git a/sound/soc/sdw_utils/soc_sdw_cs42l43.c b/sound/soc/sdw_utils/soc_sdw_cs42l43.c index 2685ff4f0932..4a451b9d4f13 100644 --- a/sound/soc/sdw_utils/soc_sdw_cs42l43.c +++ b/sound/soc/sdw_utils/soc_sdw_cs42l43.c @@ -107,6 +107,7 @@ EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_hs_rtd_init, "SND_SOC_SDW_UTILS"); int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { + struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; struct snd_soc_card *card = rtd->card; struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); @@ -131,8 +132,15 @@ int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_so ret = snd_soc_dapm_add_routes(dapm, cs42l43_spk_map, ARRAY_SIZE(cs42l43_spk_map)); - if (ret) + if (ret) { dev_err(card->dev, "cs42l43 speaker map addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_component_set_sysclk(component, CS42L43_SYSCLK, CS42L43_SYSCLK_SDW, + 0, SND_SOC_CLOCK_IN); + if (ret) + dev_err(card->dev, "Failed to set sysclk: %d\n", ret); return ret; } diff --git a/sound/soc/sdw_utils/soc_sdw_cs47l47.c b/sound/soc/sdw_utils/soc_sdw_cs47l47.c new file mode 100644 index 000000000000..259ecf1e0a71 --- /dev/null +++ b/sound/soc/sdw_utils/soc_sdw_cs47l47.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Based on sof_sdw_cs42l45.c +// This file incorporates work covered by the following copyright notice: +// Copyright (c) 2023 Intel Corporation +// Copyright (c) 2024 Advanced Micro Devices, Inc. + +/* + * soc_sdw_cs47l47 - Helpers to handle CS47L47 from generic machine driver + */ +#include <linux/device.h> +#include <linux/errno.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-card.h> +#include <sound/soc-component.h> +#include <sound/soc-dai.h> +#include <sound/soc_sdw_utils.h> + +static struct snd_soc_jack_pin soc_jack_pins[] = { + { + .pin = "cs47l47 OT 43 Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "cs47l47 OT 45 Headset", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "cs47l47 IT 31 Microphone", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "cs47l47 IT 33 Headset", + .mask = SND_JACK_MICROPHONE, + }, +}; + +int asoc_sdw_cs47l47_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_jack *jack = &ctx->sdw_headset; + int ret; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs47l47", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_card_jack_new_pins(card, "Jack", SND_JACK_MECHANICAL | + SND_JACK_HEADSET | SND_JACK_LINEOUT, jack, + soc_jack_pins, ARRAY_SIZE(soc_jack_pins)); + if (ret) { + dev_err(card->dev, "Failed to create jack: %d\n", ret); + return ret; + } + + ret = snd_soc_component_set_jack(component, jack, NULL); + if (ret) { + dev_err(card->dev, "Failed to register jack: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS(asoc_sdw_cs47l47_hs_rtd_init, "SND_SOC_SDW_UTILS"); + +int asoc_sdw_cs47l47_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = rtd->card; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s mic:cs47l47-dmic", + card->components); + if (!card->components) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_NS(asoc_sdw_cs47l47_dmic_rtd_init, "SND_SOC_SDW_UTILS"); diff --git a/sound/soc/sdw_utils/soc_sdw_rt_dmic.c b/sound/soc/sdw_utils/soc_sdw_rt_dmic.c index 97be110a59b6..fed85bad9e88 100644 --- a/sound/soc/sdw_utils/soc_sdw_rt_dmic.c +++ b/sound/soc/sdw_utils/soc_sdw_rt_dmic.c @@ -9,15 +9,20 @@ #include <linux/device.h> #include <linux/errno.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> #include <sound/soc.h> #include <sound/soc-acpi.h> #include <sound/soc_sdw_utils.h> +#include <sound/sdca_function.h> int asoc_sdw_rt_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct snd_soc_component *component; + struct sdw_slave *sdw_peripheral = NULL; char *mic_name; + int rt1320_dmic_num = 0, part_id, i; component = dai->component; @@ -32,9 +37,44 @@ int asoc_sdw_rt_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_da if (!mic_name) return -ENOMEM; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s mic:%s", card->components, - mic_name); + /* + * If there is any rt1320/rt1321 DMIC belonging to this card, try to count the `cfg-mics` + * to be used in card->components. + * Note: The rt1320 drivers register the peripheral dev to component->dev, so get the + * sdw_peripheral from component->dev. + */ + if (is_sdw_slave(component->dev)) + sdw_peripheral = dev_to_sdw_dev(component->dev); + if (sdw_peripheral && + (sdw_peripheral->id.part_id == 0x1320 || sdw_peripheral->id.part_id == 0x1321)) { + part_id = sdw_peripheral->id.part_id; + /* + * This rtd init callback is called once, so count the rt1320/rt1321 with SDCA + * function SmartMic type in this card. + */ + for_each_card_components(card, component) { + if (!is_sdw_slave(component->dev)) + continue; + sdw_peripheral = dev_to_sdw_dev(component->dev); + if (sdw_peripheral->id.part_id != part_id) + continue; + for (i = 0; i < sdw_peripheral->sdca_data.num_functions; i++) { + if (sdw_peripheral->sdca_data.function[i].type == + SDCA_FUNCTION_TYPE_SMART_MIC) { + rt1320_dmic_num++; + break; + } + } + } + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s mic:%s cfg-mics:%d", card->components, + mic_name, rt1320_dmic_num); + } else { + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s mic:%s", card->components, + mic_name); + } + if (!card->components) return -ENOMEM; diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 0e67d9f34cba..2807f536eef0 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -73,6 +73,7 @@ static const struct snd_kcontrol_new rt700_controls[] = { struct asoc_sdw_codec_info codec_info_list[] = { { + .vendor_id = 0x0102, .part_id = 0x0000, /* TAS2783A */ .name_prefix = "tas2783", .dais = { @@ -92,6 +93,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x700, .name_prefix = "rt700", .dais = { @@ -110,6 +112,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x711, .name_prefix = "rt711", .version_id = 3, @@ -131,6 +134,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x711, .name_prefix = "rt711", .version_id = 2, @@ -152,6 +156,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x712, .name_prefix = "rt712", .version_id = 3, @@ -194,6 +199,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 3, }, { + .vendor_id = 0x025d, .part_id = 0x1712, .name_prefix = "rt712-dmic", .version_id = 3, @@ -209,6 +215,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x713, .name_prefix = "rt713", .version_id = 3, @@ -237,6 +244,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 2, }, { + .vendor_id = 0x025d, .part_id = 0x1713, .name_prefix = "rt713-dmic", .version_id = 3, @@ -252,6 +260,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x1308, .name_prefix = "rt1308", .acpi_id = "10EC1308", @@ -275,6 +284,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .ops = &soc_sdw_rt1308_i2s_ops, }, { + .vendor_id = 0x025d, .part_id = 0x1316, .name_prefix = "rt1316", .dais = { @@ -296,6 +306,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x1318, .name_prefix = "rt1318", .dais = { @@ -317,8 +328,10 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x1320, .name_prefix = "rt1320", + .is_amp = true, .dais = { { .direction = {true, false}, @@ -334,12 +347,24 @@ struct asoc_sdw_codec_info codec_info_list[] = { .widgets = generic_spk_widgets, .num_widgets = ARRAY_SIZE(generic_spk_widgets), }, + { + .direction = {false, true}, + .dai_name = "rt1320-aif2", + .component_name = "rt1320", + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_rt_dmic_rtd_init, + .widgets = generic_dmic_widgets, + .num_widgets = ARRAY_SIZE(generic_dmic_widgets), + }, }, - .dai_num = 1, + .dai_num = 2, }, { + .vendor_id = 0x025d, .part_id = 0x1321, .name_prefix = "rt1320", + .is_amp = true, .dais = { { .direction = {true, false}, @@ -355,10 +380,21 @@ struct asoc_sdw_codec_info codec_info_list[] = { .widgets = generic_spk_widgets, .num_widgets = ARRAY_SIZE(generic_spk_widgets), }, + { + .direction = {false, true}, + .dai_name = "rt1320-aif2", + .component_name = "rt1320", + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_rt_dmic_rtd_init, + .widgets = generic_dmic_widgets, + .num_widgets = ARRAY_SIZE(generic_dmic_widgets), + }, }, - .dai_num = 1, + .dai_num = 2, }, { + .vendor_id = 0x025d, .part_id = 0x714, .name_prefix = "rt714", .version_id = 3, @@ -375,6 +411,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x715, .name_prefix = "rt715", .version_id = 3, @@ -391,6 +428,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x714, .name_prefix = "rt714", .version_id = 2, @@ -407,6 +445,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x715, .name_prefix = "rt715", .version_id = 2, @@ -423,6 +462,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x721, .name_prefix = "rt721", .version_id = 3, @@ -466,6 +506,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 3, }, { + .vendor_id = 0x025d, .part_id = 0x722, .name_prefix = "rt722", .version_id = 3, @@ -513,6 +554,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 3, }, { + .vendor_id = 0x019f, .part_id = 0x8373, .name_prefix = "Left", .dais = { @@ -533,6 +575,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x019f, .part_id = 0x8363, .name_prefix = "Left", .dais = { @@ -553,6 +596,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x5682, .name_prefix = "rt5682", .dais = { @@ -571,6 +615,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x01fa, .part_id = 0x3556, .name_prefix = "AMP", .dais = { @@ -598,6 +643,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 2, }, { + .vendor_id = 0x01fa, .part_id = 0x3557, .name_prefix = "AMP", .dais = { @@ -625,6 +671,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 2, }, { + .vendor_id = 0x01fa, .part_id = 0x3563, .name_prefix = "AMP", .dais = { @@ -652,6 +699,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 2, }, { + .vendor_id = 0x01fa, .part_id = 0x4242, .name_prefix = "cs42l42", .dais = { @@ -670,6 +718,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x01fa, .part_id = 0x4243, .name_prefix = "cs42l43", .count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar, @@ -724,12 +773,105 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 4, }, { + .vendor_id = 0x01fa, + .part_id = 0x2A3B, + .name_prefix = "cs42l43", + .count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar, + .add_sidecar = asoc_sdw_bridge_cs35l56_add_sidecar, + .dais = { + { + .direction = {true, false}, + .codec_name = "cs42l43-codec", + .dai_name = "cs42l43-dp5", + .dai_type = SOC_SDW_DAI_TYPE_JACK, + .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .rtd_init = asoc_sdw_cs42l43_hs_rtd_init, + .controls = generic_jack_controls, + .num_controls = ARRAY_SIZE(generic_jack_controls), + .widgets = generic_jack_widgets, + .num_widgets = ARRAY_SIZE(generic_jack_widgets), + }, + { + .direction = {false, true}, + .codec_name = "cs42l43-codec", + .dai_name = "cs42l43-dp1", + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_cs42l43_dmic_rtd_init, + .widgets = generic_dmic_widgets, + .num_widgets = ARRAY_SIZE(generic_dmic_widgets), + .quirk = SOC_SDW_CODEC_MIC, + .quirk_exclude = true, + }, + { + .direction = {false, true}, + .codec_name = "cs42l43-codec", + .dai_name = "cs42l43-dp2", + .dai_type = SOC_SDW_DAI_TYPE_JACK, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, + }, + { + .direction = {true, false}, + .codec_name = "cs42l43-codec", + .dai_name = "cs42l43-dp6", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .init = asoc_sdw_cs42l43_spk_init, + .rtd_init = asoc_sdw_cs42l43_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), + .quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS, + }, + }, + .dai_num = 4, + }, + { + .vendor_id = 0x01fa, .part_id = 0x4245, .name_prefix = "cs42l45", .dais = { { .direction = {true, false}, - .codec_name = "snd_soc_sdca.UAJ.1", + .codec_name = "snd_soc_sdca.UAJ", + .dai_name = "IT 41", + .dai_type = SOC_SDW_DAI_TYPE_JACK, + .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .rtd_init = asoc_sdw_cs42l45_hs_rtd_init, + }, + { + .direction = {false, true}, + .codec_name = "snd_soc_sdca.SmartMic", + .dai_name = "OT 113", + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_cs42l45_dmic_rtd_init, + }, + { + .direction = {false, true}, + .codec_name = "snd_soc_sdca.UAJ", + .dai_name = "OT 36", + .dai_type = SOC_SDW_DAI_TYPE_JACK, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, + }, + }, + .dai_num = 3, + .auxs = { + { + .codec_name = "snd_soc_sdca.HID", + }, + }, + .aux_num = 1, + }, + { + .vendor_id = 0x01fa, + .part_id = 0x4249, + .name_prefix = "cs42l45", /* Use same user-space as cs42l45 */ + .dais = { + { + .direction = {true, false}, + .codec_name = "snd_soc_sdca.UAJ", .dai_name = "IT 41", .dai_type = SOC_SDW_DAI_TYPE_JACK, .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, @@ -737,7 +879,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, { .direction = {false, true}, - .codec_name = "snd_soc_sdca.SmartMic.0", + .codec_name = "snd_soc_sdca.SmartMic", .dai_name = "OT 113", .dai_type = SOC_SDW_DAI_TYPE_MIC, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, @@ -745,7 +887,44 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, { .direction = {false, true}, - .codec_name = "snd_soc_sdca.UAJ.1", + .codec_name = "snd_soc_sdca.UAJ", + .dai_name = "OT 36", + .dai_type = SOC_SDW_DAI_TYPE_JACK, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, + }, + }, + .dai_num = 3, + .auxs = { + { + .codec_name = "snd_soc_sdca.HID", + }, + }, + .aux_num = 1, + }, + { + .vendor_id = 0x01fa, + .part_id = 0x4747, + .name_prefix = "cs47l47", + .dais = { + { + .direction = {true, false}, + .codec_name = "snd_soc_sdca.UAJ", + .dai_name = "IT 41", + .dai_type = SOC_SDW_DAI_TYPE_JACK, + .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .rtd_init = asoc_sdw_cs47l47_hs_rtd_init, + }, + { + .direction = {false, true}, + .codec_name = "snd_soc_sdca.SmartMic", + .dai_name = "OT 113", + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_cs47l47_dmic_rtd_init, + }, + { + .direction = {false, true}, + .codec_name = "snd_soc_sdca.UAJ", .dai_name = "OT 36", .dai_type = SOC_SDW_DAI_TYPE_JACK, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, @@ -754,12 +933,13 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 3, .auxs = { { - .codec_name = "snd_soc_sdca.HID.2", + .codec_name = "snd_soc_sdca.HID", }, }, .aux_num = 1, }, { + .vendor_id = 0x0105, .part_id = 0xaaaa, /* generic codec mockup */ .name_prefix = "sdw_mockup_mmulti-function", .version_id = 0, @@ -786,6 +966,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 3, }, { + .vendor_id = 0x0105, .part_id = 0xaa55, /* headset codec mockup */ .name_prefix = "sdw_mockup_headset0", .version_id = 0, @@ -800,6 +981,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x0105, .part_id = 0x55aa, /* amplifier mockup */ .name_prefix = "sdw_mockup_amp1", .version_id = 0, @@ -814,6 +996,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x0105, .part_id = 0x5555, .name_prefix = "sdw_mockup_mic0", .version_id = 0, @@ -838,9 +1021,10 @@ EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, "SND_SOC_SDW_UTILS"); struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr) { - unsigned int part_id, sdw_version; + unsigned int vendor_id, part_id, sdw_version; int i; + vendor_id = SDW_MFG_ID(adr); part_id = SDW_PART_ID(adr); sdw_version = SDW_VERSION(adr); for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) @@ -849,6 +1033,7 @@ struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr) * version_id is not specified in the codec info. */ if (part_id == codec_info_list[i].part_id && + vendor_id == codec_info_list[i].vendor_id && (!codec_info_list[i].version_id || sdw_version == codec_info_list[i].version_id)) return &codec_info_list[i]; @@ -863,6 +1048,7 @@ static struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_sdw_id(const struct for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) if (id->part_id == codec_info_list[i].part_id && + id->mfg_id == codec_info_list[i].vendor_id && (!codec_info_list[i].version_id || id->sdw_version == codec_info_list[i].version_id)) return &codec_info_list[i]; @@ -967,8 +1153,8 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls, codec_info->dais[dai_index].num_controls); if (ret) { - dev_err(card->dev, "%#x controls addition failed: %d\n", - codec_info->part_id, ret); + dev_err(card->dev, "%#x-%#x controls addition failed: %d\n", + codec_info->vendor_id, codec_info->part_id, ret); return ret; } } @@ -977,8 +1163,8 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) codec_info->dais[dai_index].widgets, codec_info->dais[dai_index].num_widgets); if (ret) { - dev_err(card->dev, "%#x widgets addition failed: %d\n", - codec_info->part_id, ret); + dev_err(card->dev, "%#x-%#x widgets addition failed: %d\n", + codec_info->vendor_id, codec_info->part_id, ret); return ret; } } @@ -1215,8 +1401,18 @@ const char *asoc_sdw_get_codec_name(struct device *dev, const struct snd_soc_acpi_link_adr *adr_link, int adr_index) { - if (dai_info->codec_name) - return devm_kstrdup(dev, dai_info->codec_name, GFP_KERNEL); + if (dai_info->codec_name) { + struct snd_soc_component *component; + + component = snd_soc_lookup_component_by_name(dai_info->codec_name); + if (component) { + dev_dbg(dev, "%s found component %s for codec_name %s\n", + __func__, component->name, dai_info->codec_name); + return devm_kstrdup(dev, component->name, GFP_KERNEL); + } else { + return devm_kstrdup(dev, dai_info->codec_name, GFP_KERNEL); + } + } return _asoc_sdw_get_codec_name(dev, adr_link, adr_index); } @@ -1528,7 +1724,17 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, return -EINVAL; for (j = 0; j < codec_info->aux_num; j++) { - soc_aux->dlc.name = codec_info->auxs[j].codec_name; + struct snd_soc_component *component; + + component = snd_soc_lookup_component_by_name(codec_info->auxs[j].codec_name); + if (component) { + dev_dbg(dev, "%s found component %s for aux name %s\n", + __func__, component->name, + codec_info->auxs[j].codec_name); + soc_aux->dlc.name = component->name; + } else { + soc_aux->dlc.name = codec_info->auxs[j].codec_name; + } soc_aux++; } diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 89f236ab3034..21492d15833f 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -342,14 +342,22 @@ int snd_soc_component_of_xlate_dai_name(struct snd_soc_component *component, return -ENOTSUPP; } -void snd_soc_component_setup_regmap(struct snd_soc_component *component) +int snd_soc_component_regmap_val_bytes(struct snd_soc_component *component) { - int val_bytes = regmap_get_val_bytes(component->regmap); + int val_bytes; /* Errors are legitimate for non-integer byte multiples */ - if (val_bytes > 0) - component->val_bytes = val_bytes; + + if (!component->regmap) + return 0; + + val_bytes = regmap_get_val_bytes(component->regmap); + if (val_bytes < 0) + return 0; + + return val_bytes; } +EXPORT_SYMBOL_GPL(snd_soc_component_regmap_val_bytes); #ifdef CONFIG_REGMAP @@ -368,7 +376,6 @@ void snd_soc_component_init_regmap(struct snd_soc_component *component, struct regmap *regmap) { component->regmap = regmap; - snd_soc_component_setup_regmap(component); } EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap); @@ -1037,8 +1044,8 @@ int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd) int i; for_each_rtd_components(rtd, i, component) { - if (component->driver->pcm_construct) { - ret = component->driver->pcm_construct(component, rtd); + if (component->driver->pcm_new) { + ret = component->driver->pcm_new(component, rtd); if (ret < 0) return soc_component_ret(component, ret); } @@ -1056,8 +1063,8 @@ void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd) return; for_each_rtd_components(rtd, i, component) - if (component->driver->pcm_destruct) - component->driver->pcm_destruct(component, rtd->pcm); + if (component->driver->pcm_free) + component->driver->pcm_free(component, rtd->pcm); } int snd_soc_pcm_component_prepare(struct snd_pcm_substream *substream) diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 7b81dffc6a93..b8402802ae78 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -69,10 +69,10 @@ static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback) snd_soc_dai_digital_mute(codec_dai, 1, stream); if (!snd_soc_dai_active(cpu_dai)) - cpu_dai->symmetric_rate = 0; + soc_pcm_set_dai_params(cpu_dai, NULL); if (!snd_soc_dai_active(codec_dai)) - codec_dai->symmetric_rate = 0; + soc_pcm_set_dai_params(codec_dai, NULL); snd_soc_link_compr_shutdown(cstream, rollback); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ff6eb6bfc63b..3fecf9fc903c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -167,15 +167,12 @@ static int dai_list_show(struct seq_file *m, void *v) { struct snd_soc_component *component; struct snd_soc_dai *dai; - - mutex_lock(&client_mutex); + guard(mutex)(&client_mutex); for_each_component(component) for_each_component_dais(component, dai) seq_printf(m, "%s\n", dai->name); - mutex_unlock(&client_mutex); - return 0; } DEFINE_SHOW_ATTRIBUTE(dai_list); @@ -183,14 +180,11 @@ DEFINE_SHOW_ATTRIBUTE(dai_list); static int component_list_show(struct seq_file *m, void *v) { struct snd_soc_component *component; - - mutex_lock(&client_mutex); + guard(mutex)(&client_mutex); for_each_component(component) seq_printf(m, "%s\n", component->name); - mutex_unlock(&client_mutex); - return 0; } DEFINE_SHOW_ATTRIBUTE(component_list); @@ -394,15 +388,24 @@ EXPORT_SYMBOL_GPL(snd_soc_lookup_component_nolocked); struct snd_soc_component *snd_soc_lookup_component(struct device *dev, const char *driver_name) { + guard(mutex)(&client_mutex); + + return snd_soc_lookup_component_nolocked(dev, driver_name); +} +EXPORT_SYMBOL_GPL(snd_soc_lookup_component); + +struct snd_soc_component *snd_soc_lookup_component_by_name(const char *component_name) +{ struct snd_soc_component *component; - mutex_lock(&client_mutex); - component = snd_soc_lookup_component_nolocked(dev, driver_name); - mutex_unlock(&client_mutex); + guard(mutex)(&client_mutex); + for_each_component(component) + if (strstr(component->name, component_name)) + return component; - return component; + return NULL; } -EXPORT_SYMBOL_GPL(snd_soc_lookup_component); +EXPORT_SYMBOL_GPL(snd_soc_lookup_component_by_name); struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, @@ -936,13 +939,9 @@ EXPORT_SYMBOL_GPL(snd_soc_find_dai); struct snd_soc_dai *snd_soc_find_dai_with_mutex( const struct snd_soc_dai_link_component *dlc) { - struct snd_soc_dai *dai; - - mutex_lock(&client_mutex); - dai = snd_soc_find_dai(dlc); - mutex_unlock(&client_mutex); + guard(mutex)(&client_mutex); - return dai; + return snd_soc_find_dai(dlc); } EXPORT_SYMBOL_GPL(snd_soc_find_dai_with_mutex); @@ -2154,8 +2153,6 @@ static void snd_soc_unbind_card(struct snd_soc_card *card) { if (snd_soc_card_is_instantiated(card)) { card->instantiated = false; - snd_soc_flush_all_delayed_work(card); - soc_cleanup_card_resources(card); } } @@ -2575,14 +2572,13 @@ int snd_soc_register_card(struct snd_soc_card *card) INIT_LIST_HEAD(&card->list); INIT_LIST_HEAD(&card->rtd_list); INIT_LIST_HEAD(&card->dapm_dirty); - INIT_LIST_HEAD(&card->dobj_list); card->instantiated = 0; mutex_init(&card->mutex); mutex_init(&card->dapm_mutex); mutex_init(&card->pcm_mutex); - mutex_lock(&client_mutex); + guard(mutex)(&client_mutex); if (card->devres_dev) { ret = devm_snd_soc_bind_card(card->devres_dev, card); @@ -2594,8 +2590,6 @@ int snd_soc_register_card(struct snd_soc_card *card) ret = snd_soc_bind_card(card); } - mutex_unlock(&client_mutex); - return ret; } EXPORT_SYMBOL_GPL(snd_soc_register_card); @@ -2608,10 +2602,11 @@ EXPORT_SYMBOL_GPL(snd_soc_register_card); */ void snd_soc_unregister_card(struct snd_soc_card *card) { - mutex_lock(&client_mutex); + guard(mutex)(&client_mutex); + snd_soc_unbind_card(card); list_del(&card->list); - mutex_unlock(&client_mutex); + dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); } EXPORT_SYMBOL_GPL(snd_soc_unregister_card); @@ -2889,8 +2884,7 @@ int snd_soc_add_component(struct snd_soc_component *component, struct snd_soc_card *card, *c; int ret; int i; - - mutex_lock(&client_mutex); + guard(mutex)(&client_mutex); if (component->driver->endianness) { for (i = 0; i < num_dai; i++) { @@ -2910,8 +2904,6 @@ int snd_soc_add_component(struct snd_soc_component *component, if (!component->regmap) component->regmap = dev_get_regmap(component->dev, NULL); - if (component->regmap) - snd_soc_component_setup_regmap(component); } /* see for_each_component */ @@ -2924,7 +2916,6 @@ err_cleanup: if (ret < 0) snd_soc_del_component_unlocked(component); - mutex_unlock(&client_mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_add_component); @@ -2964,7 +2955,8 @@ void snd_soc_unregister_component_by_driver(struct device *dev, if (component_driver) driver_name = component_driver->name; - mutex_lock(&client_mutex); + guard(mutex)(&client_mutex); + while (1) { struct snd_soc_component *component = snd_soc_lookup_component_nolocked(dev, driver_name); @@ -2973,7 +2965,6 @@ void snd_soc_unregister_component_by_driver(struct device *dev, snd_soc_del_component_unlocked(component); } - mutex_unlock(&client_mutex); } EXPORT_SYMBOL_GPL(snd_soc_unregister_component_by_driver); @@ -3509,7 +3500,6 @@ EXPORT_SYMBOL_GPL(snd_soc_get_stream_cpu); int snd_soc_get_dai_id(struct device_node *ep) { - struct snd_soc_component *component; struct snd_soc_dai_link_component dlc = { .of_node = of_graph_get_port_parent(ep), }; @@ -3523,11 +3513,13 @@ int snd_soc_get_dai_id(struct device_node *ep) * Then, it should have .of_xlate_dai_id */ ret = -ENOTSUPP; - mutex_lock(&client_mutex); - component = soc_find_component(&dlc); - if (component) - ret = snd_soc_component_of_xlate_dai_id(component, ep); - mutex_unlock(&client_mutex); + + scoped_guard(mutex, &client_mutex) { + struct snd_soc_component *component = soc_find_component(&dlc); + + if (component) + ret = snd_soc_component_of_xlate_dai_id(component, ep); + } of_node_put(dlc.of_node); @@ -3539,8 +3531,8 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_ { struct snd_soc_component *pos; int ret = -EPROBE_DEFER; + guard(mutex)(&client_mutex); - mutex_lock(&client_mutex); for_each_component(pos) { struct device_node *component_of_node = soc_component_to_node(pos); @@ -3595,7 +3587,6 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_ if (ret == 0) dlc->of_node = args->np; - mutex_unlock(&client_mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_get_dlc); @@ -3650,17 +3641,14 @@ struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_a { struct snd_soc_dai *dai; struct snd_soc_component *component; + guard(mutex)(&client_mutex); - mutex_lock(&client_mutex); for_each_component(component) { for_each_component_dais(component, dai) if (snd_soc_is_match_dai_args(dai->driver->dai_args, dai_args)) - goto found; + return dai; } - dai = NULL; -found: - mutex_unlock(&client_mutex); - return dai; + return NULL; } EXPORT_SYMBOL_GPL(snd_soc_get_dai_via_args); diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index a1e05307067d..2f370fda1266 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -283,6 +283,46 @@ err: EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); /** + * snd_soc_dai_set_tdm_idle() - Configure a DAI's TDM idle mode + * @dai: The DAI to configure + * @tx_mask: bitmask representing idle TX slots. + * @rx_mask: bitmask representing idle RX slots. + * @tx_mode: idle mode to set for TX slots. + * @rx_mode: idle mode to set for RX slots. + * + * This function configures the DAI to handle idle TDM slots in the + * specified manner. @tx_mode and @rx_mode can be one of + * SND_SOC_DAI_TDM_IDLE_NONE, SND_SOC_DAI_TDM_IDLE_ZERO, + * SND_SOC_DAI_TDM_IDLE_PULLDOWN, or SND_SOC_DAI_TDM_IDLE_HIZ. + * SND_SOC_TDM_IDLE_NONE represents the DAI's default/unset idle slot + * handling state and could be any of the other modes depending on the + * hardware behind the DAI. It is therefore undefined behaviour when set + * explicitly. + * + * Mode and mask can be set independently for both the TX and RX direction. + * Some hardware may ignore both TX and RX masks depending on its + * capabilities. + */ +int snd_soc_dai_set_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode) +{ + int ret = -EOPNOTSUPP; + + /* You can't write to the RX line */ + if (rx_mode == SND_SOC_DAI_TDM_IDLE_ZERO) + return soc_dai_ret(dai, -EINVAL); + + if (dai->driver->ops && + dai->driver->ops->set_tdm_idle) + ret = dai->driver->ops->set_tdm_idle(dai, tx_mask, rx_mask, + tx_mode, rx_mode); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_idle); + +/** * snd_soc_dai_set_channel_map - configure DAI audio channel map * @dai: DAI * @tx_num: how many TX channels diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2768ba5bfc9f..d6192204e613 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -89,6 +89,7 @@ static int dapm_up_seq[] = { [snd_soc_dapm_input] = 6, [snd_soc_dapm_output] = 6, [snd_soc_dapm_mux] = 7, + [snd_soc_dapm_mux_named_ctl] = 7, [snd_soc_dapm_demux] = 7, [snd_soc_dapm_dac] = 8, [snd_soc_dapm_switch] = 9, @@ -140,6 +141,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_micbias] = 10, [snd_soc_dapm_vmid] = 10, [snd_soc_dapm_mux] = 11, + [snd_soc_dapm_mux_named_ctl] = 11, [snd_soc_dapm_demux] = 11, [snd_soc_dapm_aif_in] = 12, [snd_soc_dapm_aif_out] = 12, @@ -577,6 +579,7 @@ static int dapm_check_dynamic_path( switch (sink->id) { case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: @@ -668,6 +671,7 @@ static int dapm_add_path( switch (wsink->id) { case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: ret = dapm_connect_mux(dapm, path, control, wsink); if (ret != 0) goto err; @@ -766,6 +770,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, break; case snd_soc_dapm_demux: case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: e = (struct soc_enum *)kcontrol->private_value; if (e->autodisable) { @@ -915,6 +920,7 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, break; case snd_soc_dapm_demux: case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: data->widget->on_val = value >> data->widget->shift; break; default: @@ -1198,6 +1204,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, wname_in_long_name = true; kcname_in_long_name = true; break; + case snd_soc_dapm_mux_named_ctl: case snd_soc_dapm_mixer_named_ctl: wname_in_long_name = false; kcname_in_long_name = true; @@ -1317,6 +1324,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) switch (w->id) { case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: dir = SND_SOC_DAPM_DIR_OUT; type = "mux"; break; @@ -2399,6 +2407,7 @@ static const char * const dapm_type_name[] = { [snd_soc_dapm_input] = "input", [snd_soc_dapm_output] = "output", [snd_soc_dapm_mux] = "mux", + [snd_soc_dapm_mux_named_ctl] = "mux_named_ctl", [snd_soc_dapm_demux] = "demux", [snd_soc_dapm_mixer] = "mixer", [snd_soc_dapm_mixer_named_ctl] = "mixer_named_ctl", @@ -3347,6 +3356,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) dapm_new_mixer(w); break; case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: case snd_soc_dapm_demux: dapm_new_mux(w); break; @@ -3834,6 +3844,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, break; case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: case snd_soc_dapm_demux: case snd_soc_dapm_switch: case snd_soc_dapm_mixer: diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index dbec46703b35..6b8c65763c82 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -332,7 +332,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component = { .hw_params = dmaengine_pcm_hw_params, .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, - .pcm_construct = dmaengine_pcm_new, + .pcm_new = dmaengine_pcm_new, .sync_stop = dmaengine_pcm_sync_stop, }; @@ -345,7 +345,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component_process = { .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, .copy = dmaengine_copy, - .pcm_construct = dmaengine_pcm_new, + .pcm_new = dmaengine_pcm_new, .sync_stop = dmaengine_pcm_sync_stop, }; diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index f966d4e13c7f..bbf277670718 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -110,37 +110,6 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); -static int sdca_soc_q78_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val, - unsigned int mask, unsigned int shift, int max, - bool sx) -{ - int val = reg_val; - - if (WARN_ON(!mc->shift)) - return -EINVAL; - - val = sign_extend32(val, mc->sign_bit); - val = (((val * 100) >> 8) / (int)mc->shift); - val -= mc->min; - - return val & mask; -} - -static unsigned int sdca_soc_q78_ctl_to_reg(struct soc_mixer_control *mc, int val, - unsigned int mask, unsigned int shift, int max) -{ - unsigned int ret_val; - int reg_val; - - if (WARN_ON(!mc->shift)) - return -EINVAL; - - reg_val = val + mc->min; - ret_val = (int)((reg_val * mc->shift) << 8) / 100; - - return ret_val & mask; -} - static int soc_mixer_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val, unsigned int mask, unsigned int shift, int max, bool sx) @@ -234,27 +203,19 @@ static int soc_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc, int mask, int max) { - unsigned int (*ctl_to_reg)(struct soc_mixer_control *, int, unsigned int, unsigned int, int); struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int val1, val_mask; unsigned int val2 = 0; bool double_r = false; int ret; - if (mc->sdca_q78) { - ctl_to_reg = sdca_soc_q78_ctl_to_reg; - val_mask = mask; - } else { - ctl_to_reg = soc_mixer_ctl_to_reg; - val_mask = mask << mc->shift; - } - ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[0], max); if (ret) return ret; - val1 = ctl_to_reg(mc, ucontrol->value.integer.value[0], + val1 = soc_mixer_ctl_to_reg(mc, ucontrol->value.integer.value[0], mask, mc->shift, max); + val_mask = mask << mc->shift; if (snd_soc_volsw_is_stereo(mc)) { ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[1], max); @@ -262,10 +223,14 @@ static int soc_put_volsw(struct snd_kcontrol *kcontrol, return ret; if (mc->reg == mc->rreg) { - val1 |= ctl_to_reg(mc, ucontrol->value.integer.value[1], mask, mc->rshift, max); + val1 |= soc_mixer_ctl_to_reg(mc, + ucontrol->value.integer.value[1], + mask, mc->rshift, max); val_mask |= mask << mc->rshift; } else { - val2 = ctl_to_reg(mc, ucontrol->value.integer.value[1], mask, mc->shift, max); + val2 = soc_mixer_ctl_to_reg(mc, + ucontrol->value.integer.value[1], + mask, mc->shift, max); double_r = true; } } @@ -289,28 +254,21 @@ static int soc_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc, int mask, int max, bool sx) { - int (*reg_to_ctl)(struct soc_mixer_control *, unsigned int, unsigned int, - unsigned int, int, bool); struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int reg_val; int val; - if (mc->sdca_q78) - reg_to_ctl = sdca_soc_q78_reg_to_ctl; - else - reg_to_ctl = soc_mixer_reg_to_ctl; - reg_val = snd_soc_component_read(component, mc->reg); - val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); + val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); ucontrol->value.integer.value[0] = val; if (snd_soc_volsw_is_stereo(mc)) { if (mc->reg == mc->rreg) { - val = reg_to_ctl(mc, reg_val, mask, mc->rshift, max, sx); + val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->rshift, max, sx); } else { reg_val = snd_soc_component_read(component, mc->rreg); - val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); + val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); } ucontrol->value.integer.value[1] = val; @@ -514,9 +472,10 @@ int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; + int val_bytes = snd_soc_component_regmap_val_bytes(component); uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = params->num_regs * component->val_bytes; + uinfo->count = params->num_regs * val_bytes; return 0; } @@ -527,18 +486,19 @@ int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; + int val_bytes = snd_soc_component_regmap_val_bytes(component); int ret; if (component->regmap) ret = regmap_raw_read(component->regmap, params->base, ucontrol->value.bytes.data, - params->num_regs * component->val_bytes); + params->num_regs * val_bytes); else ret = -EINVAL; /* Hide any masked bytes to ensure consistent data reporting */ if (ret == 0 && params->mask) { - switch (component->val_bytes) { + switch (val_bytes) { case 1: ucontrol->value.bytes.data[0] &= ~params->mask; break; @@ -564,13 +524,14 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; + int val_bytes = snd_soc_component_regmap_val_bytes(component); unsigned int val, mask; int ret, len; if (!component->regmap || !params->num_regs) return -EINVAL; - len = params->num_regs * component->val_bytes; + len = params->num_regs * val_bytes; void *data __free(kfree) = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); @@ -589,7 +550,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, val &= params->mask; - switch (component->val_bytes) { + switch (val_bytes) { case 1: ((u8 *)data)[0] &= ~params->mask; ((u8 *)data)[0] |= val; @@ -712,9 +673,10 @@ int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; + int val_bytes = snd_soc_component_regmap_val_bytes(component); unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; - unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; + unsigned int regwshift = val_bytes * BITS_PER_BYTE; unsigned int regwmask = GENMASK(regwshift - 1, 0); unsigned long mask = GENMASK(mc->nbits - 1, 0); long val = 0; @@ -756,9 +718,10 @@ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; + int val_bytes = snd_soc_component_regmap_val_bytes(component); unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; - unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; + unsigned int regwshift = val_bytes * BITS_PER_BYTE; unsigned int regwmask = GENMASK(regwshift - 1, 0); unsigned long mask = GENMASK(mc->nbits - 1, 0); long val = ucontrol->value.integer.value[0]; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index afa9fad4457f..9b12eedb77c3 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -423,8 +423,8 @@ void dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event) snd_soc_dapm_stream_event(fe, dir, event); } -static void soc_pcm_set_dai_params(struct snd_soc_dai *dai, - struct snd_pcm_hw_params *params) +void soc_pcm_set_dai_params(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params) { if (params) { dai->symmetric_rate = params_rate(params); diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 064b8d76b955..85679c8e0229 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -224,8 +224,11 @@ static inline void soc_control_err(struct soc_tplg *tplg, struct snd_soc_tplg_ctl_hdr *hdr, const char *name) { dev_err(tplg->dev, - "ASoC: no complete control IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n", - name, hdr->ops.get, hdr->ops.put, hdr->ops.info, + "ASoC: no complete control IO handler for %s type (g,p,i) %u:%u:%u at 0x%lx\n", + name, + le32_to_cpu(hdr->ops.get), + le32_to_cpu(hdr->ops.put), + le32_to_cpu(hdr->ops.info), soc_tplg_get_offset(tplg)); } @@ -238,17 +241,18 @@ static int soc_tplg_vendor_load(struct soc_tplg *tplg, if (tplg->ops && tplg->ops->vendor_load) ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr); else { - dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", - hdr->vendor_type); + dev_err(tplg->dev, "ASoC: no vendor load callback for ID %u\n", + le32_to_cpu(hdr->vendor_type)); return -EINVAL; } if (ret < 0) dev_err(tplg->dev, - "ASoC: vendor load failed at hdr offset %ld/0x%lx for type %d:%d\n", + "ASoC: vendor load failed at hdr offset %ld/0x%lx for type %u:%u\n", soc_tplg_get_hdr_offset(tplg), soc_tplg_get_hdr_offset(tplg), - hdr->type, hdr->vendor_type); + le32_to_cpu(hdr->type), + le32_to_cpu(hdr->vendor_type)); return ret; } @@ -625,8 +629,8 @@ static int soc_tplg_create_tlv(struct soc_tplg *tplg, /* TODO: add support for other TLV types */ default: - dev_dbg(tplg->dev, "Unsupported TLV type %d\n", - tplg_tlv->type); + dev_dbg(tplg->dev, "Unsupported TLV type %u\n", + le32_to_cpu(tplg_tlv->type)); return -EINVAL; } } @@ -653,7 +657,7 @@ static int soc_tplg_control_dmixer_create(struct soc_tplg *tplg, struct snd_kcon tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) + le32_to_cpu(mc->priv.size); dev_dbg(tplg->dev, "ASoC: adding mixer kcontrol %s with access 0x%x\n", - mc->hdr.name, mc->hdr.access); + mc->hdr.name, le32_to_cpu(mc->hdr.access)); kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL); if (!kc->name) @@ -776,7 +780,7 @@ static int soc_tplg_control_denum_create(struct soc_tplg *tplg, struct snd_kcont tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + le32_to_cpu(ec->priv.size)); - dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", ec->hdr.name, ec->items); + dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %u\n", ec->hdr.name, le32_to_cpu(ec->items)); kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL); if (!kc->name) @@ -811,8 +815,8 @@ static int soc_tplg_control_denum_create(struct soc_tplg *tplg, struct snd_kcont } break; default: - dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", - ec->hdr.ops.info, ec->hdr.name); + dev_err(tplg->dev, "ASoC: invalid enum control type %u for %s\n", + le32_to_cpu(ec->hdr.ops.info), ec->hdr.name); return -EINVAL; } @@ -846,7 +850,7 @@ static int soc_tplg_control_dbytes_create(struct soc_tplg *tplg, struct snd_kcon tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + le32_to_cpu(be->priv.size)); dev_dbg(tplg->dev, "ASoC: adding bytes kcontrol %s with access 0x%x\n", - be->hdr.name, be->hdr.access); + be->hdr.name, le32_to_cpu(be->hdr.access)); kc->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL); if (!kc->name) @@ -976,7 +980,7 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, int ret; int i; - dev_dbg(tplg->dev, "ASoC: adding %d kcontrols at 0x%lx\n", hdr->count, + dev_dbg(tplg->dev, "ASoC: adding %u kcontrols at 0x%lx\n", le32_to_cpu(hdr->count), soc_tplg_get_offset(tplg)); for (i = 0; i < le32_to_cpu(hdr->count); i++) { @@ -1003,8 +1007,8 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, } if (ret < 0) { - dev_err(tplg->dev, "ASoC: invalid control type: %d, index: %d at 0x%lx\n", - control_hdr->type, i, soc_tplg_get_offset(tplg)); + dev_err(tplg->dev, "ASoC: invalid control type: %u, index: %d at 0x%lx\n", + le32_to_cpu(control_hdr->type), i, soc_tplg_get_offset(tplg)); return ret; } } @@ -1040,8 +1044,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, count, le32_to_cpu(hdr->payload_size), "graph")) return -EINVAL; - dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count, - hdr->index); + dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %u\n", count, + le32_to_cpu(hdr->index)); for (i = 0; i < count; i++) { route = devm_kzalloc(tplg->dev, sizeof(*route), GFP_KERNEL); @@ -1116,8 +1120,8 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) return -EINVAL; - dev_dbg(tplg->dev, "ASoC: creating DAPM widget %s id %d\n", - w->name, w->id); + dev_dbg(tplg->dev, "ASoC: creating DAPM widget %s id %u\n", + w->name, le32_to_cpu(w->id)); memset(&template, 0, sizeof(template)); @@ -1200,8 +1204,9 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, goto hdr_err; break; default: - dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n", - control_hdr->ops.get, control_hdr->ops.put, + dev_err(tplg->dev, "ASoC: invalid widget control type %u:%u:%u\n", + le32_to_cpu(control_hdr->ops.get), + le32_to_cpu(control_hdr->ops.put), le32_to_cpu(control_hdr->ops.info)); ret = -EINVAL; goto hdr_err; @@ -1745,8 +1750,8 @@ static int soc_tplg_link_config(struct soc_tplg *tplg, link = snd_soc_find_dai_link(tplg->comp->card, le32_to_cpu(cfg->id), name, stream_name); if (!link) { - dev_err(tplg->dev, "ASoC: physical link %s (id %d) not exist\n", - name, cfg->id); + dev_err(tplg->dev, "ASoC: physical link %s (id %u) not exist\n", + name, le32_to_cpu(cfg->id)); return -EINVAL; } @@ -1949,7 +1954,7 @@ static int soc_tplg_valid_header(struct soc_tplg *tplg, { if (le32_to_cpu(hdr->size) != sizeof(*hdr)) { dev_err(tplg->dev, - "ASoC: invalid header size for type %d at offset 0x%lx size 0x%zx.\n", + "ASoC: invalid header size for type %u at offset 0x%lx size 0x%zx.\n", le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg), tplg->fw->size); return -EINVAL; @@ -1957,9 +1962,9 @@ static int soc_tplg_valid_header(struct soc_tplg *tplg, if (soc_tplg_get_hdr_offset(tplg) + le32_to_cpu(hdr->payload_size) >= tplg->fw->size) { dev_err(tplg->dev, - "ASoC: invalid header of type %d at offset %ld payload_size %d\n", + "ASoC: invalid header of type %u at offset %ld payload_size %u\n", le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg), - hdr->payload_size); + le32_to_cpu(hdr->payload_size)); return -EINVAL; } @@ -1967,7 +1972,7 @@ static int soc_tplg_valid_header(struct soc_tplg *tplg, if (le32_to_cpu(hdr->magic) == SOC_TPLG_MAGIC_BIG_ENDIAN) { dev_err(tplg->dev, "ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n", - tplg->pass, hdr->magic, + tplg->pass, le32_to_cpu(hdr->magic), soc_tplg_get_hdr_offset(tplg), tplg->fw->size); return -EINVAL; } @@ -1975,7 +1980,7 @@ static int soc_tplg_valid_header(struct soc_tplg *tplg, if (le32_to_cpu(hdr->magic) != SND_SOC_TPLG_MAGIC) { dev_err(tplg->dev, "ASoC: pass %d does not have a valid header got %x at offset 0x%lx size 0x%zx.\n", - tplg->pass, hdr->magic, + tplg->pass, le32_to_cpu(hdr->magic), soc_tplg_get_hdr_offset(tplg), tplg->fw->size); return -EINVAL; } @@ -1985,7 +1990,7 @@ static int soc_tplg_valid_header(struct soc_tplg *tplg, le32_to_cpu(hdr->abi) < SND_SOC_TPLG_ABI_VERSION_MIN) { dev_err(tplg->dev, "ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n", - tplg->pass, hdr->abi, + tplg->pass, le32_to_cpu(hdr->abi), SND_SOC_TPLG_ABI_VERSION, soc_tplg_get_hdr_offset(tplg), tplg->fw->size); return -EINVAL; @@ -2054,9 +2059,11 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, if (tplg->pass == hdr_pass) { dev_dbg(tplg->dev, - "ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n", - hdr->payload_size, hdr->type, hdr->version, - hdr->vendor_type, tplg->pass); + "ASoC: Got 0x%x bytes of type %u version %u vendor %u at pass %d\n", + le32_to_cpu(hdr->payload_size), + le32_to_cpu(hdr->type), + le32_to_cpu(hdr->version), + le32_to_cpu(hdr->vendor_type), tplg->pass); return elem_load(tplg, hdr); } diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index 96570121aae0..93f2376585db 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -247,14 +247,15 @@ static int sof_compr_set_params(struct snd_soc_component *component, ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[cstream->direction], ipc_params_reply.posn_offset); if (ret < 0) { - dev_err(component->dev, "Invalid stream data offset for Compr %d\n", - spcm->pcm.pcm_id); + dev_err(component->dev, "Invalid stream data offset for Compr %u\n", + le32_to_cpu(spcm->pcm.pcm_id)); goto out; } sstream->sampling_rate = params->codec.sample_rate; sstream->channels = params->codec.ch_out; sstream->sample_container_bytes = pcm->params.sample_container_bytes; + sstream->codec_params = params->codec; spcm->prepared[cstream->direction] = true; @@ -267,9 +268,10 @@ out: static int sof_compr_get_params(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_codec *params) { - /* TODO: we don't query the supported codecs for now, if the - * application asks for an unsupported codec the set_params() will fail. - */ + struct sof_compr_stream *sstream = cstream->runtime->private_data; + + *params = sstream->codec_params; + return 0; } @@ -379,6 +381,9 @@ static int sof_compr_pointer(struct snd_soc_component *component, if (!spcm) return -EINVAL; + if (!sstream->channels || !sstream->sample_container_bytes) + return -EBUSY; + tstamp->sampling_rate = sstream->sampling_rate; tstamp->copied_total = sstream->copied_total; tstamp->pcm_io_frames = div_u64(spcm->stream[cstream->direction].posn.dai_posn, diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 1c04b5d9c0d8..5c1f3b427cdb 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -480,16 +480,20 @@ int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_st struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params) { - struct hdac_stream *hstream = &hext_stream->hstream; - int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + struct hdac_stream *hstream; + int sd_offset; int ret; - u32 mask = 0x1 << hstream->index; + u32 mask; if (!hext_stream) { dev_err(sdev->dev, "error: no stream available\n"); return -ENODEV; } + hstream = &hext_stream->hstream; + sd_offset = SOF_STREAM_SD_OFFSET(hstream); + mask = 0x1 << hstream->index; + if (!dmab) { dev_err(sdev->dev, "error: no dma buffer allocated!\n"); return -ENODEV; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 8a240dcb7fcb..b3d61d973ce4 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1178,6 +1178,9 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, struct snd_soc_acpi_endpoint *endpoints; int amp_group_id = 1; + if (sdw_device->id.mfg_id != codec_info_list[i].vendor_id) + continue; + if (sdw_device->id.part_id != codec_info_list[i].part_id) continue; @@ -1192,8 +1195,8 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, * dereference */ if (!name_prefix) { - dev_err(dev, "codec_info_list name_prefix of part id %#x is missing\n", - codec_info_list[i].part_id); + dev_err(dev, "codec_info_list name_prefix of part id %#x-%#x is missing\n", + codec_info_list[i].vendor_id, codec_info_list[i].part_id); return NULL; } for (j = 0; j < codec_info_list[i].dai_num; j++) { @@ -1227,6 +1230,16 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, return NULL; } + /* + * codec_info_list[].is_amp is a codec-level override: for multi-function + * codecs we must treat the whole codec as an AMP when it is described as + * such in the codec info table, even if some endpoints were detected as + * non-AMP above. Callers/UCM rely on this to keep name_prefix and AMP + * indexing stable and backwards compatible. + */ + if (codec_info_list[i].is_amp) + is_amp = true; + adr_dev[index].adr = ((u64)sdw_device->id.class_id & 0xFF) | ((u64)sdw_device->id.part_id & 0xFFFF) << 8 | ((u64)sdw_device->id.mfg_id & 0xFFFF) << 24 | diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index c12ffdcfe4e3..76812d8fb567 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -581,6 +581,7 @@ sof_ipc4_update_card_components_string(struct snd_sof_widget *swidget, struct snd_soc_component *scomp = spcm->scomp; struct snd_soc_card *card = scomp->card; const char *pt_marker = "iec61937-pcm"; + unsigned pcm_id = le32_to_cpu(spcm->pcm.pcm_id); /* * Update the card's components list with iec61937-pcm and a list of PCM @@ -595,21 +596,21 @@ sof_ipc4_update_card_components_string(struct snd_sof_widget *swidget, if (strstr(card->components, pt_marker)) card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s,%d", + "%s,%u", card->components, - spcm->pcm.pcm_id); + pcm_id); else card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s %s:%d", + "%s %s:%u", card->components, pt_marker, - spcm->pcm.pcm_id); + pcm_id); devm_kfree(card->dev, tmp); } else { card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s:%d", pt_marker, - spcm->pcm.pcm_id); + "%s:%u", pt_marker, + pcm_id); } if (!card->components) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 5b598d0940eb..b2071edeaea6 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -360,8 +360,8 @@ static int sof_pcm_prepare(struct snd_soc_component *component, platform_params = &spcm->platform_params[substream->stream]; ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); if (ret < 0) { - dev_err(sdev->dev, "failed widget list set up for pcm %d dir %d\n", - spcm->pcm.pcm_id, dir); + dev_err(sdev->dev, "failed widget list set up for pcm %d dir %u\n", + le32_to_cpu(spcm->pcm.pcm_id), dir); spcm->stream[dir].list = NULL; snd_soc_dapm_dai_free_widgets(&list); return ret; @@ -651,8 +651,8 @@ static int sof_pcm_new(struct snd_soc_component *component, return 0; } - dev_dbg(spcm->scomp->dev, "pcm%u (%s): Entry: pcm_construct\n", - spcm->pcm.pcm_id, spcm->pcm.pcm_name); + dev_dbg(spcm->scomp->dev, "pcm%u (%s): Entry: pcm_new\n", + le32_to_cpu(spcm->pcm.pcm_id), spcm->pcm.pcm_name); /* do we need to pre-allocate playback audio buffer pages */ if (!spcm->pcm.playback) @@ -850,7 +850,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->compress_ops = &sof_compressed_ops; #endif - pd->pcm_construct = sof_pcm_new; + pd->pcm_new = sof_pcm_new; pd->ignore_machine = drv_name; pd->be_pcm_base = SOF_BE_PCM_BASE; pd->use_dai_pcm_id = true; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 36082e764bf9..138e5fcc2dd0 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -411,11 +411,11 @@ struct snd_sof_dai_link { struct snd_sof_tuple *tuples; int num_tuples; struct snd_soc_dai_link *link; - struct snd_soc_tplg_hw_config *hw_configs; int num_hw_configs; int default_hw_cfg_id; int type; struct list_head list; + struct snd_soc_tplg_hw_config hw_configs[] __counted_by(num_hw_configs); }; /* ASoC SOF DAPM widget */ @@ -641,17 +641,20 @@ void snd_sof_pcm_init_elapsed_work(struct work_struct *work); */ #define spcm_dbg(__spcm, __dir, __fmt, ...) \ dev_dbg((__spcm)->scomp->dev, "pcm%u (%s), dir %d: " __fmt, \ - (__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \ + le32_to_cpu((__spcm)->pcm.pcm_id), \ + (__spcm)->pcm.pcm_name, __dir, \ ##__VA_ARGS__) #define spcm_dbg_ratelimited(__spcm, __dir, __fmt, ...) \ dev_dbg_ratelimited((__spcm)->scomp->dev, "pcm%u (%s), dir %d: " __fmt, \ - (__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \ - ##__VA_ARGS__) + le32_to_cpu((__spcm)->pcm.pcm_id), \ + (__spcm)->pcm.pcm_name, __dir, \ + ##__VA_ARGS__) #define spcm_err(__spcm, __dir, __fmt, ...) \ dev_err((__spcm)->scomp->dev, "%s: pcm%u (%s), dir %d: " __fmt, \ - __func__, (__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \ + __func__, le32_to_cpu((__spcm)->pcm.pcm_id), \ + (__spcm)->pcm.pcm_name, __dir, \ ##__VA_ARGS__) #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 693d063830fa..38753b088fc1 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -17,6 +17,7 @@ #include <sound/sof/info.h> #include <sound/sof/pm.h> #include <sound/sof/trace.h> +#include <sound/compress_params.h> #include <uapi/sound/sof/fw.h> #include <sound/sof/ext_manifest.h> @@ -111,6 +112,7 @@ struct sof_compr_stream { u32 sampling_rate; u16 channels; u16 sample_container_bytes; + struct snd_codec codec_params; size_t posn_offset; }; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 35200d801fb7..63d582c65891 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -775,8 +775,8 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp, array); break; default: - dev_err(scomp->dev, "error: unknown token type %d\n", - array->type); + dev_err(scomp->dev, "error: unknown token type %u\n", + le32_to_cpu(array->type)); return -EINVAL; } @@ -880,7 +880,7 @@ skip: ARRAY_SIZE(led_tokens), mc->priv.array, le32_to_cpu(mc->priv.size)); if (ret != 0) { - dev_err(scomp->dev, "error: parse led tokens failed %d\n", + dev_err(scomp->dev, "error: parse led tokens failed %u\n", le32_to_cpu(mc->priv.size)); goto err; } @@ -970,8 +970,8 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, struct snd_sof_control *scontrol; int ret; - dev_dbg(scomp->dev, "tplg: load control type %d name : %s\n", - hdr->type, hdr->name); + dev_dbg(scomp->dev, "tplg: load control type %u name : %s\n", + le32_to_cpu(hdr->type), hdr->name); scontrol = kzalloc_obj(*scontrol); if (!scontrol) @@ -1015,8 +1015,10 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: case SND_SOC_TPLG_DAPM_CTL_PIN: default: - dev_warn(scomp->dev, "control type not supported %d:%d:%d\n", - hdr->ops.get, hdr->ops.put, hdr->ops.info); + dev_warn(scomp->dev, "control type not supported %u:%u:%u\n", + le32_to_cpu(hdr->ops.get), + le32_to_cpu(hdr->ops.put), + le32_to_cpu(hdr->ops.info)); kfree(scontrol->name); kfree(scontrol); return 0; @@ -1523,8 +1525,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, break; case snd_soc_dapm_pga: if (!le32_to_cpu(tw->num_kcontrols)) { - dev_err(scomp->dev, "invalid kcontrol count %d for volume\n", - tw->num_kcontrols); + dev_err(scomp->dev, "invalid kcontrol count %u for volume\n", + le32_to_cpu(tw->num_kcontrols)); ret = -EINVAL; break; } @@ -1772,7 +1774,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, ARRAY_SIZE(stream_tokens), private->array, le32_to_cpu(private->size)); if (ret) { - dev_err(scomp->dev, "error: parse stream tokens failed %d\n", + dev_err(scomp->dev, "error: parse stream tokens failed %u\n", le32_to_cpu(private->size)); return ret; } @@ -1906,18 +1908,12 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ return -EINVAL; } - slink = kzalloc_obj(*slink); + slink = kzalloc_flex(*slink, hw_configs, le32_to_cpu(cfg->num_hw_configs)); if (!slink) return -ENOMEM; slink->num_hw_configs = le32_to_cpu(cfg->num_hw_configs); - slink->hw_configs = kmemdup_array(cfg->hw_config, - slink->num_hw_configs, sizeof(*slink->hw_configs), - GFP_KERNEL); - if (!slink->hw_configs) { - kfree(slink); - return -ENOMEM; - } + memcpy(slink->hw_configs, cfg->hw_config, le32_to_cpu(cfg->num_hw_configs) * sizeof(*slink->hw_configs)); slink->default_hw_cfg_id = le32_to_cpu(cfg->default_hw_config_id); slink->link = link; @@ -1930,7 +1926,6 @@ 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->hw_configs); kfree(slink); return ret; } @@ -2001,7 +1996,6 @@ 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->hw_configs); kfree(slink); return -ENOMEM; } @@ -2059,7 +2053,6 @@ out: err: kfree(slink->tuples); - kfree(slink->hw_configs); kfree(slink); return ret; @@ -2076,7 +2069,6 @@ static int sof_link_unload(struct snd_soc_component *scomp, struct snd_soc_dobj kfree(slink->tuples); list_del(&slink->list); - kfree(slink->hw_configs); kfree(slink); dobj->private = NULL; diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index 4ad8b1fc713a..b31b120a85de 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA SoC SPDIF In Audio Layer for spear processors * * Copyright (C) 2012 ST Microelectronics * Vipin Kumar <vipin.kumar@st.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index 469373d1bb41..c06f09c646a8 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA SoC SPDIF Out Audio Layer for spear processors * * Copyright (C) 2012 ST Microelectronics * Vipin Kumar <vipin.kumar@st.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c index e8476da157cd..f222956e857c 100644 --- a/sound/soc/spear/spear_pcm.c +++ b/sound/soc/spear/spear_pcm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA PCM interface for ST SPEAr Processors * @@ -5,10 +6,6 @@ * * Copyright (C) 2012 ST Microelectronics * Rajeev Kumar<rajeevkumar.linux@gmail.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/module.h> diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c index d6b96cc2f708..cbf5bf82d96e 100644 --- a/sound/soc/sprd/sprd-pcm-dma.c +++ b/sound/soc/sprd/sprd-pcm-dma.c @@ -453,7 +453,7 @@ static const struct snd_soc_component_driver sprd_soc_component = { .hw_free = sprd_pcm_hw_free, .trigger = sprd_pcm_trigger, .pointer = sprd_pcm_pointer, - .pcm_construct = sprd_pcm_new, + .pcm_new = sprd_pcm_new, .compress_ops = &sprd_platform_compress_ops, }; diff --git a/sound/soc/starfive/Makefile b/sound/soc/starfive/Makefile index 9e958f70ef51..cfe03c83d5f7 100644 --- a/sound/soc/starfive/Makefile +++ b/sound/soc/starfive/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # StarFive Platform Support obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index 6d1ce030963c..45d35b887e4e 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -1028,8 +1028,13 @@ static int uni_player_parse_dt_audio_glue(struct platform_device *pdev, return PTR_ERR(regmap); } - player->clk_sel = regmap_field_alloc(regmap, regfield[0]); - player->valid_sel = regmap_field_alloc(regmap, regfield[1]); + player->clk_sel = devm_regmap_field_alloc(&pdev->dev, regmap, regfield[0]); + if (IS_ERR(player->clk_sel)) + return PTR_ERR(player->clk_sel); + + player->valid_sel = devm_regmap_field_alloc(&pdev->dev, regmap, regfield[1]); + if (IS_ERR(player->valid_sel)) + return PTR_ERR(player->valid_sel); return 0; } diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 1797a91fea7a..0f6d32814c22 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -315,7 +315,7 @@ static const struct snd_soc_component_driver stm32_adfsdm_soc_platform = { .hw_params = stm32_adfsdm_pcm_hw_params, .trigger = stm32_adfsdm_trigger, .pointer = stm32_adfsdm_pcm_pointer, - .pcm_construct = stm32_adfsdm_pcm_new, + .pcm_new = stm32_adfsdm_pcm_new, }; static const struct of_device_id stm32_adfsdm_of_match[] = { diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index 65de03ca3ad2..c2ec19437cd7 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -712,15 +712,10 @@ static int sun4i_spdif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); if (quirks->has_reset) { - host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, - NULL); - if (PTR_ERR(host->rst) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); - return ret; - } - if (!IS_ERR(host->rst)) - reset_control_deassert(host->rst); + host->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); + if (IS_ERR(host->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->rst), + "Failed to get reset\n"); } ret = devm_snd_soc_register_component(&pdev->dev, diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 9dbd589879fb..fdc954028d62 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -229,6 +229,16 @@ config SND_SOC_TEGRA_WM8903 boards using the WM8093 codec. Currently, the supported boards are Harmony, Ventana, Seaboard, Kaen, and Aebl. +config SND_SOC_TEGRA_WM8962 + tristate "SoC Audio support for Tegra boards using a WM8962 codec" + depends on I2C && INPUT && GPIOLIB + select SND_SOC_TEGRA_MACHINE_DRV + select SND_SOC_WM8962 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the WM8962 codec. Currently, the supported boards are + Microsoft Surface RT. + config SND_SOC_TEGRA_WM9712 tristate "SoC Audio support for Tegra boards using a WM9712 codec" depends on GPIOLIB @@ -294,6 +304,15 @@ config SND_SOC_TEGRA_SGTL5000 boards using the SGTL5000 codec, such as Apalis T30, Apalis TK1 or Colibri T30. +config SND_SOC_TEGRA_CPCAP + tristate "SoC Audio support for Tegra boards using a CPCAP codec" + depends on I2C && GPIOLIB && MFD_CPCAP + select SND_SOC_TEGRA_MACHINE_DRV + select SND_SOC_CPCAP + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the CPCAP codec, such as Motorola Atrix 4G or Droid X2. + endif endmenu diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index defea7f53f11..3f396c87802e 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -43,9 +43,11 @@ obj-$(CONFIG_SND_SOC_TEGRA210_OPE) += snd-soc-tegra210-ope.o # Tegra machine Support snd-soc-tegra-wm8903-y := tegra_wm8903.o +snd-soc-tegra-wm8962-y := tegra_wm8962.o snd-soc-tegra-machine-y := tegra_asoc_machine.o snd-soc-tegra-audio-graph-card-y := tegra_audio_graph_card.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o +obj-$(CONFIG_SND_SOC_TEGRA_WM8962) += snd-soc-tegra-wm8962.o obj-$(CONFIG_SND_SOC_TEGRA_MACHINE_DRV) += snd-soc-tegra-machine.o obj-$(CONFIG_SND_SOC_TEGRA_AUDIO_GRAPH_CARD) += snd-soc-tegra-audio-graph-card.o diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c index d2a5ec7c54cc..7135aa23a7fc 100644 --- a/sound/soc/tegra/tegra186_asrc.c +++ b/sound/soc/tegra/tegra186_asrc.c @@ -989,10 +989,9 @@ static int tegra186_asrc_platform_probe(struct platform_device *pdev) asrc->regmap = devm_regmap_init_mmio(dev, regs, &tegra186_asrc_regmap_config); - if (IS_ERR(asrc->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(asrc->regmap); - } + if (IS_ERR(asrc->regmap)) + return dev_err_probe(dev, PTR_ERR(asrc->regmap), + "regmap init failed\n"); asrc->soc_data = of_device_get_match_data(&pdev->dev); @@ -1016,10 +1015,9 @@ static int tegra186_asrc_platform_probe(struct platform_device *pdev) err = devm_snd_soc_register_component(dev, &tegra186_asrc_cmpnt, tegra186_asrc_dais, ARRAY_SIZE(tegra186_asrc_dais)); - if (err) { - dev_err(dev, "can't register ASRC component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register ASRC component\n"); pm_runtime_enable(dev); diff --git a/sound/soc/tegra/tegra186_dspk.c b/sound/soc/tegra/tegra186_dspk.c index 8816e4967331..7cf7d6dbfc35 100644 --- a/sound/soc/tegra/tegra186_dspk.c +++ b/sound/soc/tegra/tegra186_dspk.c @@ -496,31 +496,27 @@ static int tegra186_dspk_platform_probe(struct platform_device *pdev) dev_set_drvdata(dev, dspk); dspk->clk_dspk = devm_clk_get(dev, "dspk"); - if (IS_ERR(dspk->clk_dspk)) { - dev_err(dev, "can't retrieve DSPK clock\n"); - return PTR_ERR(dspk->clk_dspk); - } + if (IS_ERR(dspk->clk_dspk)) + return dev_err_probe(dev, PTR_ERR(dspk->clk_dspk), + "can't retrieve DSPK clock\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); dspk->regmap = devm_regmap_init_mmio(dev, regs, &tegra186_dspk_regmap); - if (IS_ERR(dspk->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(dspk->regmap); - } + if (IS_ERR(dspk->regmap)) + return dev_err_probe(dev, PTR_ERR(dspk->regmap), + "regmap init failed\n"); regcache_cache_only(dspk->regmap, true); err = devm_snd_soc_register_component(dev, &tegra186_dspk_cmpnt, tegra186_dspk_dais, ARRAY_SIZE(tegra186_dspk_dais)); - if (err) { - dev_err(dev, "can't register DSPK component, err: %d\n", - err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register DSPK component\n"); pm_runtime_enable(dev); diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h index ff4b79e2052f..26ab8d5c7f99 100644 --- a/sound/soc/tegra/tegra20_spdif.h +++ b/sound/soc/tegra/tegra20_spdif.h @@ -171,7 +171,7 @@ /* * RX channel block data receive status: - * 0=entire block not recieved yet. + * 0=entire block not received yet. * 1=received entire block of channel status, */ #define TEGRA20_SPDIF_STATUS_IS_C (1 << 21) diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c index 0976779d29f2..a1c2757a3932 100644 --- a/sound/soc/tegra/tegra210_admaif.c +++ b/sound/soc/tegra/tegra210_admaif.c @@ -408,6 +408,7 @@ static int tegra_admaif_start(struct snd_soc_dai *dai, int direction) reg = CH_RX_REG(TEGRA_ADMAIF_RX_ENABLE, dai->id); break; default: + dev_err(dai->dev, "invalid stream direction: %d\n", direction); return -EINVAL; } @@ -441,6 +442,7 @@ static int tegra_admaif_stop(struct snd_soc_dai *dai, int direction) reset_reg = CH_RX_REG(TEGRA_ADMAIF_RX_SOFT_RESET, dai->id); break; default: + dev_err(dai->dev, "invalid stream direction: %d\n", direction); return -EINVAL; } @@ -489,6 +491,7 @@ static int tegra_admaif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_SUSPEND: return tegra_admaif_stop(dai, substream->stream); default: + dev_err(dai->dev, "invalid trigger command: %d\n", cmd); return -EINVAL; } } @@ -839,7 +842,7 @@ static struct snd_kcontrol_new tegra264_admaif_controls[] = { static const struct snd_soc_component_driver tegra210_admaif_cmpnt = { .controls = tegra210_admaif_controls, .num_controls = ARRAY_SIZE(tegra210_admaif_controls), - .pcm_construct = tegra_pcm_construct, + .pcm_new = tegra_pcm_new, .open = tegra_pcm_open, .close = tegra_pcm_close, .hw_params = tegra_pcm_hw_params, @@ -849,7 +852,7 @@ static const struct snd_soc_component_driver tegra210_admaif_cmpnt = { static const struct snd_soc_component_driver tegra186_admaif_cmpnt = { .controls = tegra186_admaif_controls, .num_controls = ARRAY_SIZE(tegra186_admaif_controls), - .pcm_construct = tegra_pcm_construct, + .pcm_new = tegra_pcm_new, .open = tegra_pcm_open, .close = tegra_pcm_close, .hw_params = tegra_pcm_hw_params, @@ -859,7 +862,7 @@ static const struct snd_soc_component_driver tegra186_admaif_cmpnt = { static const struct snd_soc_component_driver tegra264_admaif_cmpnt = { .controls = tegra264_admaif_controls, .num_controls = ARRAY_SIZE(tegra264_admaif_controls), - .pcm_construct = tegra_pcm_construct, + .pcm_new = tegra_pcm_new, .open = tegra_pcm_open, .close = tegra_pcm_close, .hw_params = tegra_pcm_hw_params, @@ -958,18 +961,15 @@ static int tegra_admaif_probe(struct platform_device *pdev) admaif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, admaif->soc_data->regmap_conf); - if (IS_ERR(admaif->regmap)) { - dev_err(&pdev->dev, "regmap init failed\n"); - return PTR_ERR(admaif->regmap); - } + if (IS_ERR(admaif->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(admaif->regmap), + "regmap init failed\n"); regcache_cache_only(admaif->regmap, true); err = tegra_isomgr_adma_register(&pdev->dev); - if (err) { - dev_err(&pdev->dev, "Failed to add interconnect path\n"); + if (err) return err; - } regmap_update_bits(admaif->regmap, admaif->soc_data->global_base + TEGRA_ADMAIF_GLOBAL_ENABLE, 1, 1); @@ -1009,11 +1009,9 @@ static int tegra_admaif_probe(struct platform_device *pdev) admaif->soc_data->cmpnt, admaif->soc_data->dais, admaif->soc_data->num_ch); - if (err) { - dev_err(&pdev->dev, - "can't register ADMAIF component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(&pdev->dev, err, + "can't register ADMAIF component\n"); pm_runtime_enable(&pdev->dev); diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c index 95875c75ddf8..d2f742ffc59d 100644 --- a/sound/soc/tegra/tegra210_adx.c +++ b/sound/soc/tegra/tegra210_adx.c @@ -134,8 +134,11 @@ static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai, memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); - if (channels < 1 || channels > adx->soc_data->max_ch) + if (channels < 1 || channels > adx->soc_data->max_ch) { + dev_err(dai->dev, "invalid channels: %u (max %u)\n", + channels, adx->soc_data->max_ch); return -EINVAL; + } switch (format) { case SNDRV_PCM_FORMAT_S8: @@ -149,6 +152,7 @@ static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai, audio_bits = TEGRA_ACIF_BITS_32; break; default: + dev_err(dai->dev, "unsupported format: %d\n", format); return -EINVAL; } @@ -693,10 +697,9 @@ static int tegra210_adx_platform_probe(struct platform_device *pdev) adx->regmap = devm_regmap_init_mmio(dev, regs, soc_data->regmap_conf); - if (IS_ERR(adx->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(adx->regmap); - } + if (IS_ERR(adx->regmap)) + return dev_err_probe(dev, PTR_ERR(adx->regmap), + "regmap init failed\n"); regcache_cache_only(adx->regmap, true); @@ -717,10 +720,9 @@ static int tegra210_adx_platform_probe(struct platform_device *pdev) err = devm_snd_soc_register_component(dev, &tegra210_adx_cmpnt, tegra210_adx_dais, ARRAY_SIZE(tegra210_adx_dais)); - if (err) { - dev_err(dev, "can't register ADX component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register ADX component\n"); pm_runtime_enable(dev); diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index 43a45f785d5b..ece33b7ff190 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -20,6 +20,7 @@ static int tegra_ahub_get_value_enum(struct snd_kcontrol *kctl, struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_to_component(kctl); struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt); struct soc_enum *e = (struct soc_enum *)kctl->private_value; + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); unsigned int reg, i, bit_pos = 0; /* @@ -35,7 +36,7 @@ static int tegra_ahub_get_value_enum(struct snd_kcontrol *kctl, if (reg_val) { bit_pos = ffs(reg_val) + - (8 * cmpnt->val_bytes * i); + (8 * val_bytes * i); break; } } @@ -59,6 +60,7 @@ static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl, struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kctl); struct soc_enum *e = (struct soc_enum *)kctl->private_value; struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { }; + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); unsigned int *item = uctl->value.enumerated.item; unsigned int value = e->values[item[0]]; unsigned int i, bit_pos, reg_idx = 0, reg_val = 0; @@ -69,8 +71,8 @@ static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl, if (value) { /* Get the register index and value to set */ - reg_idx = (value - 1) / (8 * cmpnt->val_bytes); - bit_pos = (value - 1) % (8 * cmpnt->val_bytes); + reg_idx = (value - 1) / (8 * val_bytes); + bit_pos = (value - 1) % (8 * val_bytes); reg_val = BIT(bit_pos); } @@ -2265,10 +2267,9 @@ static int tegra_ahub_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ahub); ahub->clk = devm_clk_get(&pdev->dev, "ahub"); - if (IS_ERR(ahub->clk)) { - dev_err(&pdev->dev, "can't retrieve AHUB clock\n"); - return PTR_ERR(ahub->clk); - } + if (IS_ERR(ahub->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(ahub->clk), + "can't retrieve AHUB clock\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) @@ -2276,10 +2277,9 @@ static int tegra_ahub_probe(struct platform_device *pdev) ahub->regmap = devm_regmap_init_mmio(&pdev->dev, regs, ahub->soc_data->regmap_config); - if (IS_ERR(ahub->regmap)) { - dev_err(&pdev->dev, "regmap init failed\n"); - return PTR_ERR(ahub->regmap); - } + if (IS_ERR(ahub->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(ahub->regmap), + "regmap init failed\n"); regcache_cache_only(ahub->regmap, true); @@ -2287,18 +2287,17 @@ static int tegra_ahub_probe(struct platform_device *pdev) ahub->soc_data->cmpnt_drv, ahub->soc_data->dai_drv, ahub->soc_data->num_dais); - if (err) { - dev_err(&pdev->dev, "can't register AHUB component, err: %d\n", - err); - return err; - } + if (err) + return dev_err_probe(&pdev->dev, err, + "can't register AHUB component\n"); pm_runtime_enable(&pdev->dev); err = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); if (err) { pm_runtime_disable(&pdev->dev); - return err; + return dev_err_probe(&pdev->dev, err, + "failed to populate child nodes\n"); } return 0; diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c index bfda82505298..d635046bbe81 100644 --- a/sound/soc/tegra/tegra210_amx.c +++ b/sound/soc/tegra/tegra210_amx.c @@ -163,6 +163,8 @@ static int tegra210_amx_set_audio_cif(struct snd_soc_dai *dai, audio_bits = TEGRA_ACIF_BITS_32; break; default: + dev_err(dai->dev, "unsupported format: %d\n", + params_format(params)); return -EINVAL; } @@ -743,10 +745,9 @@ static int tegra210_amx_platform_probe(struct platform_device *pdev) amx->regmap = devm_regmap_init_mmio(dev, regs, amx->soc_data->regmap_conf); - if (IS_ERR(amx->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(amx->regmap); - } + if (IS_ERR(amx->regmap)) + return dev_err_probe(dev, PTR_ERR(amx->regmap), + "regmap init failed\n"); regcache_cache_only(amx->regmap, true); @@ -767,10 +768,9 @@ static int tegra210_amx_platform_probe(struct platform_device *pdev) err = devm_snd_soc_register_component(dev, &tegra210_amx_cmpnt, tegra210_amx_dais, ARRAY_SIZE(tegra210_amx_dais)); - if (err) { - dev_err(dev, "can't register AMX component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register AMX component\n"); pm_runtime_enable(dev); diff --git a/sound/soc/tegra/tegra210_dmic.c b/sound/soc/tegra/tegra210_dmic.c index 93def7ac4fde..3e42e2c75eb9 100644 --- a/sound/soc/tegra/tegra210_dmic.c +++ b/sound/soc/tegra/tegra210_dmic.c @@ -507,10 +507,9 @@ static int tegra210_dmic_probe(struct platform_device *pdev) dev_set_drvdata(dev, dmic); dmic->clk_dmic = devm_clk_get(dev, "dmic"); - if (IS_ERR(dmic->clk_dmic)) { - dev_err(dev, "can't retrieve DMIC clock\n"); - return PTR_ERR(dmic->clk_dmic); - } + if (IS_ERR(dmic->clk_dmic)) + return dev_err_probe(dev, PTR_ERR(dmic->clk_dmic), + "can't retrieve DMIC clock\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) @@ -518,20 +517,18 @@ static int tegra210_dmic_probe(struct platform_device *pdev) dmic->regmap = devm_regmap_init_mmio(dev, regs, &tegra210_dmic_regmap_config); - if (IS_ERR(dmic->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(dmic->regmap); - } + if (IS_ERR(dmic->regmap)) + return dev_err_probe(dev, PTR_ERR(dmic->regmap), + "regmap init failed\n"); regcache_cache_only(dmic->regmap, true); err = devm_snd_soc_register_component(dev, &tegra210_dmic_compnt, tegra210_dmic_dais, ARRAY_SIZE(tegra210_dmic_dais)); - if (err) { - dev_err(dev, "can't register DMIC component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register DMIC component\n"); pm_runtime_enable(dev); diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index d8e02f0a3025..0259b137547c 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -161,6 +161,7 @@ static int tegra210_i2s_init(struct snd_soc_dapm_widget *w, stream = SNDRV_PCM_STREAM_CAPTURE; status_reg = TEGRA210_I2S_TX_STATUS + i2s->soc_data->tx_offset; } else { + dev_err(dev, "invalid I2S direction register 0x%x\n", w->reg); return -EINVAL; } @@ -235,6 +236,7 @@ static int tegra210_i2s_set_fmt(struct snd_soc_dai *dai, val = I2S_CTRL_MASTER_EN; break; default: + dev_err(dai->dev, "invalid clock provider format 0x%x\n", fmt); return -EINVAL; } @@ -270,6 +272,7 @@ static int tegra210_i2s_set_fmt(struct snd_soc_dai *dai, tegra210_i2s_set_data_offset(i2s, 0); break; default: + dev_err(dai->dev, "invalid I2S frame format 0x%x\n", fmt); return -EINVAL; } @@ -290,6 +293,7 @@ static int tegra210_i2s_set_fmt(struct snd_soc_dai *dai, val ^= I2S_CTRL_LRCK_POL_MASK; break; default: + dev_err(dai->dev, "invalid I2S clock inversion 0x%x\n", fmt); return -EINVAL; } @@ -1070,10 +1074,9 @@ static int tegra210_i2s_probe(struct platform_device *pdev) dev_set_drvdata(dev, i2s); i2s->clk_i2s = devm_clk_get(dev, "i2s"); - if (IS_ERR(i2s->clk_i2s)) { - dev_err(dev, "can't retrieve I2S bit clock\n"); - return PTR_ERR(i2s->clk_i2s); - } + if (IS_ERR(i2s->clk_i2s)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_i2s), + "can't retrieve I2S bit clock\n"); /* * Not an error, as this clock is needed only when some other I/O @@ -1090,10 +1093,9 @@ static int tegra210_i2s_probe(struct platform_device *pdev) i2s->regmap = devm_regmap_init_mmio(dev, regs, i2s->soc_data->regmap_conf); - if (IS_ERR(i2s->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(i2s->regmap); - } + if (IS_ERR(i2s->regmap)) + return dev_err_probe(dev, PTR_ERR(i2s->regmap), + "regmap init failed\n"); tegra210_parse_client_convert(dev); @@ -1108,10 +1110,9 @@ static int tegra210_i2s_probe(struct platform_device *pdev) err = devm_snd_soc_register_component(dev, i2s->soc_data->i2s_cmpnt, tegra210_i2s_dais, ARRAY_SIZE(tegra210_i2s_dais)); - if (err) { - dev_err(dev, "can't register I2S component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register I2S component\n"); pm_runtime_enable(dev); diff --git a/sound/soc/tegra/tegra210_mbdrc.c b/sound/soc/tegra/tegra210_mbdrc.c index 6a268dbb7197..f5d4a93dd6f1 100644 --- a/sound/soc/tegra/tegra210_mbdrc.c +++ b/sound/soc/tegra/tegra210_mbdrc.c @@ -307,13 +307,14 @@ static int tegra210_mbdrc_band_params_get(struct snd_kcontrol *kcontrol, struct tegra_soc_bytes *params = (void *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); u32 *data = (u32 *)ucontrol->value.bytes.data; u32 regs = params->soc.base; u32 mask = params->soc.mask; u32 shift = params->shift; unsigned int i; - for (i = 0; i < params->soc.num_regs; i++, regs += cmpnt->val_bytes) { + for (i = 0; i < params->soc.num_regs; i++, regs += val_bytes) { regmap_read(ope->mbdrc_regmap, regs, &data[i]); data[i] = ((data[i] & mask) >> shift); @@ -328,6 +329,7 @@ static int tegra210_mbdrc_band_params_put(struct snd_kcontrol *kcontrol, struct tegra_soc_bytes *params = (void *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); u32 *data = (u32 *)ucontrol->value.bytes.data; u32 regs = params->soc.base; u32 mask = params->soc.mask; @@ -335,7 +337,7 @@ static int tegra210_mbdrc_band_params_put(struct snd_kcontrol *kcontrol, bool change = false; unsigned int i; - for (i = 0; i < params->soc.num_regs; i++, regs += cmpnt->val_bytes) { + for (i = 0; i < params->soc.num_regs; i++, regs += val_bytes) { bool update = false; regmap_update_bits_check(ope->mbdrc_regmap, regs, mask, @@ -353,13 +355,14 @@ static int tegra210_mbdrc_threshold_get(struct snd_kcontrol *kcontrol, struct tegra_soc_bytes *params = (void *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); u32 *data = (u32 *)ucontrol->value.bytes.data; u32 regs = params->soc.base; u32 num_regs = params->soc.num_regs; u32 val; unsigned int i; - for (i = 0; i < num_regs; i += 4, regs += cmpnt->val_bytes) { + for (i = 0; i < num_regs; i += 4, regs += val_bytes) { regmap_read(ope->mbdrc_regmap, regs, &val); data[i] = (val & TEGRA210_MBDRC_THRESH_1ST_MASK) >> @@ -381,13 +384,14 @@ static int tegra210_mbdrc_threshold_put(struct snd_kcontrol *kcontrol, struct tegra_soc_bytes *params = (void *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); u32 *data = (u32 *)ucontrol->value.bytes.data; u32 regs = params->soc.base; u32 num_regs = params->soc.num_regs; bool change = false; unsigned int i; - for (i = 0; i < num_regs; i += 4, regs += cmpnt->val_bytes) { + for (i = 0; i < num_regs; i += 4, regs += val_bytes) { bool update = false; data[i] = (((data[i] >> TEGRA210_MBDRC_THRESH_1ST_SHIFT) & @@ -413,9 +417,10 @@ static int tegra210_mbdrc_biquad_coeffs_get(struct snd_kcontrol *kcontrol, { struct tegra_soc_bytes *params = (void *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); u32 *data = (u32 *)ucontrol->value.bytes.data; - memset(data, 0, params->soc.num_regs * cmpnt->val_bytes); + memset(data, 0, params->soc.num_regs * val_bytes); return 0; } @@ -426,8 +431,9 @@ static int tegra210_mbdrc_biquad_coeffs_put(struct snd_kcontrol *kcontrol, struct tegra_soc_bytes *params = (void *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); u32 reg_ctrl = params->soc.base; - u32 reg_data = reg_ctrl + cmpnt->val_bytes; + u32 reg_data = reg_ctrl + val_bytes; u32 *data = (u32 *)ucontrol->value.bytes.data; tegra210_mbdrc_write_ram(ope->mbdrc_regmap, reg_ctrl, reg_data, @@ -988,14 +994,14 @@ int tegra210_mbdrc_regmap_init(struct platform_device *pdev) child = of_get_child_by_name(dev->of_node, "dynamic-range-compressor"); if (!child) - return -ENODEV; + return dev_err_probe(dev, -ENODEV, + "missing 'dynamic-range-compressor' DT child node\n"); err = of_address_to_resource(child, 0, &mem); of_node_put(child); - if (err < 0) { - dev_err(dev, "fail to get MBDRC resource\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, + "failed to get MBDRC resource\n"); mem.flags = IORESOURCE_MEM; regs = devm_ioremap_resource(dev, &mem); @@ -1004,10 +1010,9 @@ int tegra210_mbdrc_regmap_init(struct platform_device *pdev) ope->mbdrc_regmap = devm_regmap_init_mmio(dev, regs, &tegra210_mbdrc_regmap_cfg); - if (IS_ERR(ope->mbdrc_regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(ope->mbdrc_regmap); - } + if (IS_ERR(ope->mbdrc_regmap)) + return dev_err_probe(dev, PTR_ERR(ope->mbdrc_regmap), + "MBDRC regmap init failed\n"); regcache_cache_only(ope->mbdrc_regmap, true); diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c index 6d3a2b76fd61..ce44117a0b9c 100644 --- a/sound/soc/tegra/tegra210_mixer.c +++ b/sound/soc/tegra/tegra210_mixer.c @@ -641,20 +641,18 @@ static int tegra210_mixer_platform_probe(struct platform_device *pdev) mixer->regmap = devm_regmap_init_mmio(dev, regs, &tegra210_mixer_regmap_config); - if (IS_ERR(mixer->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(mixer->regmap); - } + if (IS_ERR(mixer->regmap)) + return dev_err_probe(dev, PTR_ERR(mixer->regmap), + "regmap init failed\n"); regcache_cache_only(mixer->regmap, true); err = devm_snd_soc_register_component(dev, &tegra210_mixer_cmpnt, tegra210_mixer_dais, ARRAY_SIZE(tegra210_mixer_dais)); - if (err) { - dev_err(dev, "can't register MIXER component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register MIXER component\n"); pm_runtime_enable(dev); diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c index 6cdc5e1f5507..2c299704ef4f 100644 --- a/sound/soc/tegra/tegra210_mvc.c +++ b/sound/soc/tegra/tegra210_mvc.c @@ -731,20 +731,18 @@ static int tegra210_mvc_platform_probe(struct platform_device *pdev) mvc->regmap = devm_regmap_init_mmio(dev, regs, &tegra210_mvc_regmap_config); - if (IS_ERR(mvc->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(mvc->regmap); - } + if (IS_ERR(mvc->regmap)) + return dev_err_probe(dev, PTR_ERR(mvc->regmap), + "regmap init failed\n"); regcache_cache_only(mvc->regmap, true); err = devm_snd_soc_register_component(dev, &tegra210_mvc_cmpnt, tegra210_mvc_dais, ARRAY_SIZE(tegra210_mvc_dais)); - if (err) { - dev_err(dev, "can't register MVC component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register MVC component\n"); pm_runtime_enable(dev); diff --git a/sound/soc/tegra/tegra210_ope.c b/sound/soc/tegra/tegra210_ope.c index a440888dcdbd..ad4c400281e8 100644 --- a/sound/soc/tegra/tegra210_ope.c +++ b/sound/soc/tegra/tegra210_ope.c @@ -318,34 +318,28 @@ static int tegra210_ope_probe(struct platform_device *pdev) ope->regmap = devm_regmap_init_mmio(dev, regs, &tegra210_ope_regmap_config); - if (IS_ERR(ope->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(ope->regmap); - } + if (IS_ERR(ope->regmap)) + return dev_err_probe(dev, PTR_ERR(ope->regmap), + "regmap init failed\n"); regcache_cache_only(ope->regmap, true); dev_set_drvdata(dev, ope); err = tegra210_peq_regmap_init(pdev); - if (err < 0) { - dev_err(dev, "PEQ init failed\n"); + if (err < 0) return err; - } err = tegra210_mbdrc_regmap_init(pdev); - if (err < 0) { - dev_err(dev, "MBDRC init failed\n"); + if (err < 0) return err; - } err = devm_snd_soc_register_component(dev, &tegra210_ope_cmpnt, tegra210_ope_dais, ARRAY_SIZE(tegra210_ope_dais)); - if (err) { - dev_err(dev, "can't register OPE component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register OPE component\n"); pm_runtime_enable(dev); diff --git a/sound/soc/tegra/tegra210_peq.c b/sound/soc/tegra/tegra210_peq.c index 2f72e9d541dc..9d5702a7ae3b 100644 --- a/sound/soc/tegra/tegra210_peq.c +++ b/sound/soc/tegra/tegra210_peq.c @@ -148,8 +148,9 @@ static int tegra210_peq_ram_get(struct snd_kcontrol *kcontrol, struct tegra_soc_bytes *params = (void *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); u32 i, reg_ctrl = params->soc.base; - u32 reg_data = reg_ctrl + cmpnt->val_bytes; + u32 reg_data = reg_ctrl + val_bytes; s32 *data = (s32 *)biquad_coeff_buffer; pm_runtime_get_sync(cmpnt->dev); @@ -171,8 +172,9 @@ static int tegra210_peq_ram_put(struct snd_kcontrol *kcontrol, struct tegra_soc_bytes *params = (void *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); u32 i, reg_ctrl = params->soc.base; - u32 reg_data = reg_ctrl + cmpnt->val_bytes; + u32 reg_data = reg_ctrl + val_bytes; s32 *data = (s32 *)biquad_coeff_buffer; for (i = 0; i < params->soc.num_regs; i++) @@ -408,14 +410,14 @@ int tegra210_peq_regmap_init(struct platform_device *pdev) child = of_get_child_by_name(dev->of_node, "equalizer"); if (!child) - return -ENODEV; + return dev_err_probe(dev, -ENODEV, + "missing 'equalizer' DT child node\n"); err = of_address_to_resource(child, 0, &mem); of_node_put(child); - if (err < 0) { - dev_err(dev, "fail to get PEQ resource\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, + "failed to get PEQ resource\n"); mem.flags = IORESOURCE_MEM; regs = devm_ioremap_resource(dev, &mem); @@ -423,10 +425,9 @@ int tegra210_peq_regmap_init(struct platform_device *pdev) return PTR_ERR(regs); ope->peq_regmap = devm_regmap_init_mmio(dev, regs, &tegra210_peq_regmap_config); - if (IS_ERR(ope->peq_regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(ope->peq_regmap); - } + if (IS_ERR(ope->peq_regmap)) + return dev_err_probe(dev, PTR_ERR(ope->peq_regmap), + "PEQ regmap init failed\n"); regcache_cache_only(ope->peq_regmap, true); diff --git a/sound/soc/tegra/tegra210_sfc.c b/sound/soc/tegra/tegra210_sfc.c index b298bf0421b1..a4b96445f1da 100644 --- a/sound/soc/tegra/tegra210_sfc.c +++ b/sound/soc/tegra/tegra210_sfc.c @@ -3598,20 +3598,18 @@ static int tegra210_sfc_platform_probe(struct platform_device *pdev) sfc->regmap = devm_regmap_init_mmio(dev, regs, &tegra210_sfc_regmap_config); - if (IS_ERR(sfc->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(sfc->regmap); - } + if (IS_ERR(sfc->regmap)) + return dev_err_probe(dev, PTR_ERR(sfc->regmap), + "regmap init failed\n"); regcache_cache_only(sfc->regmap, true); err = devm_snd_soc_register_component(dev, &tegra210_sfc_cmpnt, tegra210_sfc_dais, ARRAY_SIZE(tegra210_sfc_dais)); - if (err) { - dev_err(dev, "can't register SFC component, err: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't register SFC component\n"); pm_runtime_enable(&pdev->dev); diff --git a/sound/soc/tegra/tegra_asoc_machine.c b/sound/soc/tegra/tegra_asoc_machine.c index d48463ac16fc..50b23bbb637b 100644 --- a/sound/soc/tegra/tegra_asoc_machine.c +++ b/sound/soc/tegra/tegra_asoc_machine.c @@ -287,6 +287,25 @@ static unsigned int tegra_machine_mclk_rate_6mhz(unsigned int srate) return mclk; } +static unsigned int tegra_machine_mclk_rate_cpcap(unsigned int srate) +{ + unsigned int mclk; + + switch (srate) { + case 11025: + case 22050: + case 44100: + case 88200: + mclk = 26000000; + break; + default: + mclk = 256 * srate; + break; + } + + return mclk; +} + static int tegra_machine_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -413,7 +432,8 @@ static int tegra_machine_register_codec(struct device *dev, const char *name) pdev = platform_device_register_simple(name, -1, NULL, 0); if (IS_ERR(pdev)) - return PTR_ERR(pdev); + return dev_err_probe(dev, PTR_ERR(pdev), + "failed to register codec %s\n", name); err = devm_add_action_or_reset(dev, tegra_machine_unregister_codec, pdev); @@ -449,32 +469,38 @@ int tegra_asoc_machine_probe(struct platform_device *pdev) gpiod = devm_gpiod_get_optional(dev, "nvidia,hp-mute", GPIOD_OUT_HIGH); machine->gpiod_hp_mute = gpiod; if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); + return dev_err_probe(dev, PTR_ERR(gpiod), + "failed to get hp-mute GPIO\n"); gpiod = devm_gpiod_get_optional(dev, "nvidia,hp-det", GPIOD_IN); machine->gpiod_hp_det = gpiod; if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); + return dev_err_probe(dev, PTR_ERR(gpiod), + "failed to get hp-det GPIO\n"); gpiod = devm_gpiod_get_optional(dev, "nvidia,mic-det", GPIOD_IN); machine->gpiod_mic_det = gpiod; if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); + return dev_err_probe(dev, PTR_ERR(gpiod), + "failed to get mic-det GPIO\n"); gpiod = devm_gpiod_get_optional(dev, "nvidia,spkr-en", GPIOD_OUT_LOW); machine->gpiod_spkr_en = gpiod; if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); + return dev_err_probe(dev, PTR_ERR(gpiod), + "failed to get spkr-en GPIO\n"); gpiod = devm_gpiod_get_optional(dev, "nvidia,int-mic-en", GPIOD_OUT_LOW); machine->gpiod_int_mic_en = gpiod; if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); + return dev_err_probe(dev, PTR_ERR(gpiod), + "failed to get int-mic-en GPIO\n"); gpiod = devm_gpiod_get_optional(dev, "nvidia,ext-mic-en", GPIOD_OUT_LOW); machine->gpiod_ext_mic_en = gpiod; if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); + return dev_err_probe(dev, PTR_ERR(gpiod), + "failed to get ext-mic-en GPIO\n"); err = snd_soc_of_parse_card_name(card, "nvidia,model"); if (err) @@ -530,22 +556,19 @@ int tegra_asoc_machine_probe(struct platform_device *pdev) card->driver_name = "tegra"; machine->clk_pll_a = devm_clk_get(dev, "pll_a"); - if (IS_ERR(machine->clk_pll_a)) { - dev_err(dev, "Can't retrieve clk pll_a\n"); - return PTR_ERR(machine->clk_pll_a); - } + if (IS_ERR(machine->clk_pll_a)) + return dev_err_probe(dev, PTR_ERR(machine->clk_pll_a), + "can't retrieve clk pll_a\n"); machine->clk_pll_a_out0 = devm_clk_get(dev, "pll_a_out0"); - if (IS_ERR(machine->clk_pll_a_out0)) { - dev_err(dev, "Can't retrieve clk pll_a_out0\n"); - return PTR_ERR(machine->clk_pll_a_out0); - } + if (IS_ERR(machine->clk_pll_a_out0)) + return dev_err_probe(dev, PTR_ERR(machine->clk_pll_a_out0), + "can't retrieve clk pll_a_out0\n"); machine->clk_cdev1 = devm_clk_get(dev, "mclk"); - if (IS_ERR(machine->clk_cdev1)) { - dev_err(dev, "Can't retrieve clk cdev1\n"); - return PTR_ERR(machine->clk_cdev1); - } + if (IS_ERR(machine->clk_cdev1)) + return dev_err_probe(dev, PTR_ERR(machine->clk_cdev1), + "can't retrieve clk cdev1\n"); /* * If clock parents are not set in DT, configure here to use clk_out_1 @@ -559,28 +582,24 @@ int tegra_asoc_machine_probe(struct platform_device *pdev) dev_warn(dev, "Please update DT to use assigned-clock-parents\n"); clk_extern1 = devm_clk_get(dev, "extern1"); - if (IS_ERR(clk_extern1)) { - dev_err(dev, "Can't retrieve clk extern1\n"); - return PTR_ERR(clk_extern1); - } + if (IS_ERR(clk_extern1)) + return dev_err_probe(dev, PTR_ERR(clk_extern1), + "can't retrieve clk extern1\n"); err = clk_set_parent(clk_extern1, machine->clk_pll_a_out0); - if (err < 0) { - dev_err(dev, "Set parent failed for clk extern1\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, + "set parent failed for clk extern1\n"); clk_out_1 = devm_clk_get(dev, "pmc_clk_out_1"); - if (IS_ERR(clk_out_1)) { - dev_err(dev, "Can't retrieve pmc_clk_out_1\n"); - return PTR_ERR(clk_out_1); - } + if (IS_ERR(clk_out_1)) + return dev_err_probe(dev, PTR_ERR(clk_out_1), + "can't retrieve pmc_clk_out_1\n"); err = clk_set_parent(clk_out_1, clk_extern1); - if (err < 0) { - dev_err(dev, "Set parent failed for pmc_clk_out_1\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, + "set parent failed for pmc_clk_out_1\n"); machine->clk_cdev1 = clk_out_1; } @@ -591,16 +610,14 @@ int tegra_asoc_machine_probe(struct platform_device *pdev) * host controller and the external codec */ err = clk_set_rate(machine->clk_pll_a, 73728000); - if (err) { - dev_err(dev, "Can't set pll_a rate: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't set pll_a rate\n"); err = clk_set_rate(machine->clk_pll_a_out0, 24576000); - if (err) { - dev_err(dev, "Can't set pll_a_out0 rate: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't set pll_a_out0 rate\n"); machine->set_baseclock = 73728000; machine->set_mclk = 24576000; @@ -612,10 +629,9 @@ int tegra_asoc_machine_probe(struct platform_device *pdev) * only needed for audio. */ err = clk_prepare_enable(machine->clk_cdev1); - if (err) { - dev_err(dev, "Can't enable cdev1: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "can't enable cdev1\n"); err = devm_snd_soc_register_card(dev, card); if (err) @@ -985,6 +1001,38 @@ static const struct tegra_asoc_data tegra_rt5631_data = { .add_hp_jack = true, }; +/* CPCAP machine */ + +SND_SOC_DAILINK_DEFS(cpcap_hifi, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cpcap-hifi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link tegra_cpcap_dai = { + .name = "CPCAP", + .stream_name = "CPCAP PCM", + .init = tegra_asoc_machine_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBP_CFP, + SND_SOC_DAILINK_REG(cpcap_hifi), +}; + +static struct snd_soc_card snd_soc_tegra_cpcap = { + .components = "codec:cpcap", + .dai_link = &tegra_cpcap_dai, + .num_links = 1, + .fully_routed = true, +}; + +static const struct tegra_asoc_data tegra_cpcap_data = { + .mclk_rate = tegra_machine_mclk_rate_cpcap, + .card = &snd_soc_tegra_cpcap, + .add_common_dapm_widgets = true, + .add_common_controls = true, + .add_common_snd_ops = true, +}; + static const struct of_device_id tegra_machine_of_match[] = { { .compatible = "nvidia,tegra-audio-trimslice", .data = &tegra_trimslice_data }, { .compatible = "nvidia,tegra-audio-max98090", .data = &tegra_max98090_data }, @@ -997,6 +1045,7 @@ static const struct of_device_id tegra_machine_of_match[] = { { .compatible = "nvidia,tegra-audio-rt5640", .data = &tegra_rt5640_data }, { .compatible = "nvidia,tegra-audio-alc5632", .data = &tegra_rt5632_data }, { .compatible = "nvidia,tegra-audio-rt5631", .data = &tegra_rt5631_data }, + { .compatible = "nvidia,tegra-audio-cpcap", .data = &tegra_cpcap_data }, {}, }; MODULE_DEVICE_TABLE(of, tegra_machine_of_match); diff --git a/sound/soc/tegra/tegra_audio_graph_card.c b/sound/soc/tegra/tegra_audio_graph_card.c index ea10e6e8a9fe..b93a61db9ed0 100644 --- a/sound/soc/tegra/tegra_audio_graph_card.c +++ b/sound/soc/tegra/tegra_audio_graph_card.c @@ -174,20 +174,23 @@ static int tegra_audio_graph_card_probe(struct snd_soc_card *card) { struct simple_util_priv *simple = snd_soc_card_get_drvdata(card); struct tegra_audio_priv *priv = simple_to_tegra_priv(simple); + int ret; priv->clk_plla = devm_clk_get(card->dev, "pll_a"); - if (IS_ERR(priv->clk_plla)) { - dev_err(card->dev, "Can't retrieve clk pll_a\n"); - return PTR_ERR(priv->clk_plla); - } + if (IS_ERR(priv->clk_plla)) + return dev_err_probe(card->dev, PTR_ERR(priv->clk_plla), + "can't retrieve clk pll_a\n"); priv->clk_plla_out0 = devm_clk_get(card->dev, "plla_out0"); - if (IS_ERR(priv->clk_plla_out0)) { - dev_err(card->dev, "Can't retrieve clk plla_out0\n"); - return PTR_ERR(priv->clk_plla_out0); - } + if (IS_ERR(priv->clk_plla_out0)) + return dev_err_probe(card->dev, PTR_ERR(priv->clk_plla_out0), + "can't retrieve clk plla_out0\n"); + + ret = graph_util_card_probe(card); + if (ret < 0) + return dev_err_probe(card->dev, ret, "graph_util_card_probe failed\n"); - return graph_util_card_probe(card); + return ret; } static int tegra_audio_graph_probe(struct platform_device *pdev) diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 05d59e03b1c5..c490a9e66858 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -204,8 +204,8 @@ static int tegra_pcm_dma_allocate(struct device *dev, struct snd_soc_pcm_runtime return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC, dev, size); } -int tegra_pcm_construct(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) +int tegra_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct device *dev = component->dev; @@ -218,7 +218,7 @@ int tegra_pcm_construct(struct snd_soc_component *component, return tegra_pcm_dma_allocate(dev, rtd, tegra_pcm_hardware.buffer_bytes_max); } -EXPORT_SYMBOL_GPL(tegra_pcm_construct); +EXPORT_SYMBOL_GPL(tegra_pcm_new); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("Tegra PCM ASoC driver"); diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index 2a36eea1740d..ad69ca9233da 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -20,8 +20,8 @@ #include <sound/dmaengine_pcm.h> #include <sound/asound.h> -int tegra_pcm_construct(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd); +int tegra_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd); int tegra_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream); int tegra_pcm_close(struct snd_soc_component *component, diff --git a/sound/soc/tegra/tegra_wm8962.c b/sound/soc/tegra/tegra_wm8962.c new file mode 100644 index 000000000000..31f9d9181595 --- /dev/null +++ b/sound/soc/tegra/tegra_wm8962.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * tegra_wm8962.c - Tegra machine ASoC driver for boards using WM8962 codec. + * + * Copyright (C) 2021-2024 Jonas Schwöbel <jonasschwoebel@yahoo.de> + * Svyatoslav Ryhel <clamor95@gmail.com> + * + * Based on tegra_wm8903 code copyright/by: + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010-2012 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. + * + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + */ + +#include <linux/gpio/consumer.h> +#include <linux/of.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/soc.h> + +#include "../codecs/wm8962.h" + +#include "tegra_asoc_machine.h" + +static struct snd_soc_jack_pin tegra_wm8962_mic_jack_pins[] = { + { .pin = "Mic Jack", .mask = SND_JACK_MICROPHONE }, +}; + +static unsigned int tegra_wm8962_mclk_rate(unsigned int srate) +{ + unsigned int mclk; + + switch (srate) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + case 64000: + case 96000: + mclk = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + mclk = 11289600; + break; + default: + mclk = 12000000; + break; + } + + return mclk; +} + +static int tegra_wm8962_init(struct snd_soc_pcm_runtime *rtd) +{ + struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_card *card = rtd->card; + struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); + int err; + + err = tegra_asoc_machine_init(rtd); + if (err) + return err; + + if (!machine->gpiod_mic_det && machine->asoc->add_mic_jack) { + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + + err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, + machine->mic_jack, + tegra_wm8962_mic_jack_pins, + ARRAY_SIZE(tegra_wm8962_mic_jack_pins)); + if (err) { + dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err); + return err; + } + + wm8962_mic_detect(component, machine->mic_jack); + } + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + + return 0; +} + +static int tegra_wm8962_remove(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link = &card->dai_link[0]; + struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card, link); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + + wm8962_mic_detect(component, NULL); + + return 0; +} + +SND_SOC_DAILINK_DEFS(wm8962_hifi, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8962")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link tegra_wm8962_dai = { + .name = "WM8962", + .stream_name = "WM8962 PCM", + .init = tegra_wm8962_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC, + SND_SOC_DAILINK_REG(wm8962_hifi), +}; + +static struct snd_soc_card snd_soc_tegra_wm8962 = { + .components = "codec:wm8962", + .owner = THIS_MODULE, + .dai_link = &tegra_wm8962_dai, + .num_links = 1, + .remove = tegra_wm8962_remove, + .fully_routed = true, +}; + +static const struct tegra_asoc_data tegra_wm8962_data = { + .mclk_rate = tegra_wm8962_mclk_rate, + .card = &snd_soc_tegra_wm8962, + .add_common_dapm_widgets = true, + .add_common_controls = true, + .add_common_snd_ops = true, + .add_mic_jack = true, + .add_hp_jack = true, +}; + +static const struct of_device_id tegra_wm8962_of_match[] = { + { .compatible = "nvidia,tegra-audio-wm8962", .data = &tegra_wm8962_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra_wm8962_of_match); + +static struct platform_driver tegra_wm8962_driver = { + .driver = { + .name = "tegra-wm8962", + .of_match_table = tegra_wm8962_of_match, + .pm = &snd_soc_pm_ops, + }, + .probe = tegra_asoc_machine_probe, +}; +module_platform_driver(tegra_wm8962_driver); + +MODULE_AUTHOR("Jonas Schwöbel <jonasschwoebel@yahoo.de>"); +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); +MODULE_DESCRIPTION("Tegra+WM8962 machine ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 2d260fbc9b83..f229f847eaf4 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -274,6 +274,14 @@ static inline unsigned int mcasp_get_auxclk_fs_ratio(struct davinci_mcasp *mcasp mcasp->auxclk_fs_ratio_tx : mcasp->auxclk_fs_ratio_rx; } +static inline bool mcasp_is_auxclk_enabled(struct davinci_mcasp *mcasp, int stream) +{ + if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_CAPTURE) + return mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG) & AHCLKRE; + + return mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE; +} + static void mcasp_start_rx(struct davinci_mcasp *mcasp) { if (mcasp->rxnumevt) { /* enable FIFO */ @@ -1337,33 +1345,42 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, int bclk_div_id, auxclk_div_id; bool auxclk_enabled; + auxclk_enabled = mcasp_is_auxclk_enabled(mcasp, stream); + if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_CAPTURE) { - auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG) & AHCLKRE; bclk_div_id = MCASP_CLKDIV_BCLK_RXONLY; auxclk_div_id = MCASP_CLKDIV_AUXCLK_RXONLY; } else if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_PLAYBACK) { - auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE; bclk_div_id = MCASP_CLKDIV_BCLK_TXONLY; auxclk_div_id = MCASP_CLKDIV_AUXCLK_TXONLY; } else { - auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE; bclk_div_id = MCASP_CLKDIV_BCLK; auxclk_div_id = MCASP_CLKDIV_AUXCLK; } - if (div > (ACLKXDIV_MASK + 1)) { - if (auxclk_enabled) { - aux_div = div / (ACLKXDIV_MASK + 1); - if (div % (ACLKXDIV_MASK + 1)) - aux_div++; + if (div > (ACLKXDIV_MASK + 1) && auxclk_enabled) { + if (div <= (AHCLKXDIV_MASK + 1)) { + /* aux_div absorbs entire division; bclk_div = 1 */ + aux_div = div; + if ((div + 1) <= (AHCLKXDIV_MASK + 1)) { + unsigned int err_lo = sysclk_freq / div - + bclk_freq; + unsigned int err_hi = bclk_freq - + sysclk_freq / (div + 1); - sysclk_freq /= aux_div; - div = sysclk_freq / bclk_freq; - rem = sysclk_freq % bclk_freq; - } else if (set) { - dev_warn(mcasp->dev, "Too fast reference clock (%u)\n", - sysclk_freq); + if (err_hi < err_lo) + aux_div = div + 1; + } + } else { + aux_div = DIV_ROUND_UP(div, ACLKXDIV_MASK + 1); } + + sysclk_freq /= aux_div; + div = sysclk_freq / bclk_freq; + rem = sysclk_freq % bclk_freq; + } else if (div > (ACLKXDIV_MASK + 1) && set) { + dev_warn(mcasp->dev, "Too fast reference clock (%u)\n", + sysclk_freq); } if (rem != 0) { @@ -2823,6 +2840,8 @@ static int davinci_mcasp_runtime_resume(struct device *dev) #endif static const struct dev_pm_ops davinci_mcasp_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(davinci_mcasp_runtime_suspend, davinci_mcasp_runtime_resume, NULL) diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c index 265d61723e99..c1ca55997103 100644 --- a/sound/soc/uniphier/aio-dma.c +++ b/sound/soc/uniphier/aio-dma.c @@ -226,7 +226,7 @@ static const struct snd_soc_component_driver uniphier_soc_platform = { .trigger = uniphier_aiodma_trigger, .pointer = uniphier_aiodma_pointer, .mmap = uniphier_aiodma_mmap, - .pcm_construct = uniphier_aiodma_new, + .pcm_new = uniphier_aiodma_new, .compress_ops = &uniphier_aio_compress_ops, }; diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 04a4eae1bc92..8f7a76758535 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -582,7 +582,7 @@ static const struct snd_soc_component_driver xlnx_asoc_component = { .hw_params = xlnx_formatter_pcm_hw_params, .trigger = xlnx_formatter_pcm_trigger, .pointer = xlnx_formatter_pcm_pointer, - .pcm_construct = xlnx_formatter_pcm_new, + .pcm_new = xlnx_formatter_pcm_new, }; static int xlnx_formatter_pcm_probe(struct platform_device *pdev) diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c index 678ded059b95..9ad86c54e3ea 100644 --- a/sound/soc/xtensa/xtfpga-i2s.c +++ b/sound/soc/xtensa/xtfpga-i2s.c @@ -481,7 +481,7 @@ static const struct snd_soc_component_driver xtfpga_i2s_component = { .hw_params = xtfpga_pcm_hw_params, .trigger = xtfpga_pcm_trigger, .pointer = xtfpga_pcm_pointer, - .pcm_construct = xtfpga_pcm_new, + .pcm_new = xtfpga_pcm_new, .legacy_dai_naming = 1, }; diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index 5ff78814e687..874f6cd503ca 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -53,11 +53,6 @@ static void usb6fire_chip_abort(struct sfire_chip *chip) usb6fire_comm_abort(chip); if (chip->control) usb6fire_control_abort(chip); - if (chip->card) { - snd_card_disconnect(chip->card); - snd_card_free_when_closed(chip->card); - chip->card = NULL; - } } } @@ -168,6 +163,7 @@ destroy_chip: static void usb6fire_chip_disconnect(struct usb_interface *intf) { struct sfire_chip *chip; + struct snd_card *card; chip = usb_get_intfdata(intf); if (chip) { /* if !chip, fw upload has been performed */ @@ -178,8 +174,19 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf) chips[chip->regidx] = NULL; } + /* + * Save card pointer before teardown. + * snd_card_free_when_closed() may free card (and + * the embedded chip) immediately, so it must be + * called last and chip must not be accessed after. + */ + card = chip->card; chip->shutdown = true; + if (card) + snd_card_disconnect(card); usb6fire_chip_abort(chip); + if (card) + snd_card_free_when_closed(card); } } } diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 3a71bab8a477..51177ebfb8c6 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -384,7 +384,7 @@ static void card_free(struct snd_card *card) snd_usb_caiaq_input_free(cdev); #endif snd_usb_caiaq_audio_free(cdev); - usb_reset_device(cdev->chip.dev); + usb_put_dev(cdev->chip.dev); } static int create_card(struct usb_device *usb_dev, @@ -410,7 +410,7 @@ static int create_card(struct usb_device *usb_dev, return err; cdev = caiaqdev(card); - cdev->chip.dev = usb_dev; + cdev->chip.dev = usb_get_dev(usb_dev); cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); diff --git a/sound/usb/card.c b/sound/usb/card.c index 270dad84d825..f42d72cd0378 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -631,9 +631,9 @@ static void usb_audio_make_shortname(struct usb_device *dev, } /* retrieve the device string as shortname */ - if (!dev->descriptor.iProduct || - usb_string(dev, dev->descriptor.iProduct, - card->shortname, sizeof(card->shortname)) <= 0) { + if (dev->product && *dev->product) { + strscpy(card->shortname, dev->product); + } else { /* no name available from anywhere, so use ID */ scnprintf(card->shortname, sizeof(card->shortname), "USB Device %#04x:%#04x", @@ -668,15 +668,11 @@ static void usb_audio_make_longname(struct usb_device *dev, else if (quirk && quirk->vendor_name) s = quirk->vendor_name; *card->longname = 0; - if (s && *s) { - strscpy(card->longname, s, sizeof(card->longname)); - } else { - /* retrieve the vendor and device strings as longname */ - if (dev->descriptor.iManufacturer) - usb_string(dev, dev->descriptor.iManufacturer, - card->longname, sizeof(card->longname)); - /* we don't really care if there isn't any vendor string */ - } + if (s && *s) + strscpy(card->longname, s); + else if (dev->manufacturer && *dev->manufacturer) + strscpy(card->longname, dev->manufacturer); + if (*card->longname) { strim(card->longname); if (*card->longname) @@ -870,19 +866,25 @@ static void find_last_interface(struct snd_usb_audio *chip) /* look for the corresponding quirk */ static const struct snd_usb_audio_quirk * -get_alias_quirk(struct usb_device *dev, unsigned int id) +get_alias_quirk(struct usb_interface *intf, unsigned int id) { const struct usb_device_id *p; + struct usb_device_id match_id; for (p = usb_audio_ids; p->match_flags; p++) { - /* FIXME: this checks only vendor:product pair in the list */ - if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) == - USB_DEVICE_ID_MATCH_DEVICE && - p->idVendor == USB_ID_VENDOR(id) && - p->idProduct == USB_ID_PRODUCT(id)) - return (const struct snd_usb_audio_quirk *)p->driver_info; - } + if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) != + USB_DEVICE_ID_MATCH_DEVICE) + continue; + if (p->idVendor != USB_ID_VENDOR(id) || + p->idProduct != USB_ID_PRODUCT(id)) + continue; + match_id = *p; + match_id.match_flags &= ~USB_DEVICE_ID_MATCH_DEVICE; + if (!match_id.match_flags || usb_match_one_id(intf, &match_id)) + return (const struct snd_usb_audio_quirk *) + p->driver_info; + } return NULL; } @@ -931,7 +933,7 @@ static int usb_audio_probe(struct usb_interface *intf, id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); if (get_alias_id(dev, &id)) - quirk = get_alias_quirk(dev, id); + quirk = get_alias_quirk(intf, id); if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) return -ENXIO; if (quirk && quirk->ifnum == QUIRK_NODEV_INTERFACE) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index bf4401aba76c..6fbcb117555c 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1379,9 +1379,6 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip, return -EINVAL; } - ep->packsize[0] = min(ep->packsize[0], ep->maxframesize); - ep->packsize[1] = min(ep->packsize[1], ep->maxframesize); - /* calculate the frequency in 16.16 format */ ep->freqm = ep->freqn; ep->freqshift = INT_MIN; @@ -1408,6 +1405,9 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip, ep->maxframesize = ep->maxpacksize / ep->cur_frame_bytes; ep->curframesize = ep->curpacksize / ep->cur_frame_bytes; + ep->packsize[0] = min(ep->packsize[0], ep->maxframesize); + ep->packsize[1] = min(ep->packsize[1], ep->maxframesize); + err = update_clock_ref_rate(chip, ep); if (err >= 0) { ep->need_setup = false; diff --git a/sound/usb/format.c b/sound/usb/format.c index 1207c507882a..030b4307927a 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -455,6 +455,10 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, if (chip->usb_id == USB_ID(0x194f, 0x010d) && !s1810c_valid_sample_rate(fp, rate)) goto skip_rate; + /* Filter out invalid rates on Presonus Studio 1824 */ + if (chip->usb_id == USB_ID(0x194f, 0x0107) && + !s1810c_valid_sample_rate(fp, rate)) + goto skip_rate; /* Filter out invalid rates on Focusrite devices */ if (USB_ID_VENDOR(chip->usb_id) == 0x1235 && diff --git a/sound/usb/midi.c b/sound/usb/midi.c index a8bddc90c0ed..0a5b8941ebda 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -699,15 +699,18 @@ static void snd_usbmidi_transmit_byte(struct usbmidi_out_port *port, static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint *ep, struct urb *urb) { - int p; + int port0 = ep->current_port; + int i; + + for (i = 0; i < 0x10; ++i) { + int portnum = (port0 + i) & 15; + struct usbmidi_out_port *port = &ep->ports[portnum]; - /* FIXME: lower-numbered ports can starve higher-numbered ports */ - for (p = 0; p < 0x10; ++p) { - struct usbmidi_out_port *port = &ep->ports[p]; if (!port->active) continue; while (urb->transfer_buffer_length + 3 < ep->max_transfer) { uint8_t b; + if (snd_rawmidi_transmit(port->substream, &b, 1) != 1) { port->active = 0; break; @@ -715,6 +718,7 @@ static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint *ep, snd_usbmidi_transmit_byte(port, b, urb); } } + ep->current_port = (port0 + 1) & 15; } static const struct usb_protocol_ops snd_usbmidi_standard_ops = { diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index ef602e81576d..3546ba926cb3 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -1057,10 +1057,8 @@ static void set_fallback_rawmidi_names(struct snd_usb_midi2_interface *umidi) strscpy(ump->core.name, ump->info.name, sizeof(ump->core.name)); /* use serial number string as unique UMP product id */ - if (!*ump->info.product_id && dev->descriptor.iSerialNumber) - usb_string(dev, dev->descriptor.iSerialNumber, - ump->info.product_id, - sizeof(ump->info.product_id)); + if (!*ump->info.product_id && dev->serial && *dev->serial) + strscpy(ump->info.product_id, dev->serial); } } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index ac8c71ba9483..aa6ea3be100a 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1204,6 +1204,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, cval->min = -11264; /* Mute under it */ } break; + case USB_ID(0x31b2, 0x0111): /* MOONDROP JU Jiu */ + if (!strcmp(kctl->id.name, "PCM Playback Volume")) { + usb_audio_info(chip, + "set volume quirk for MOONDROP JU Jiu\n"); + cval->min = -10880; /* Mute under it */ + } + break; } } @@ -1226,12 +1233,79 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) } /* + * Additional checks for sticky mixers + * + * Some devices' volume control mixers are sticky, which accept SET_CUR but + * do absolutely nothing. + * + * Prevent sticky mixers from being registered, otherwise they confuses + * userspace and results in ineffective volume control. + */ +static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, + int channel, int saved) +{ + int sticky_test_values[] = { cval->min, cval->max }; + int test, check, i; + + for (i = 0; i < ARRAY_SIZE(sticky_test_values); i++) { + test = sticky_test_values[i]; + if (test == saved) + continue; + + /* Assume non-sticky on failure. */ + if (snd_usb_set_cur_mix_value(cval, channel, 0, test) || + get_cur_mix_raw(cval, channel, &check) || + check != saved) /* SET_CUR effective, non-sticky. */ + return 0; + } + + usb_audio_err(cval->head.mixer->chip, + "%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n", + cval->head.id, mixer_ctrl_intf(cval->head.mixer), + cval->min, cval->max, cval->res, saved); + + return -ENODEV; +} + +/* + * Additional checks for the proper resolution + * + * Some devices report smaller resolutions than actually reacting. + * They don't return errors but simply clip to the lower aligned value. + */ +static void check_volume_control_res(struct usb_mixer_elem_info *cval, + int channel, int saved) +{ + int last_valid_res = cval->res; + int test, check; + + for (;;) { + test = saved; + if (test < cval->max) + test += cval->res; + else + test -= cval->res; + + if (test < cval->min || test > cval->max || + snd_usb_set_cur_mix_value(cval, channel, 0, test) || + get_cur_mix_raw(cval, channel, &check)) { + cval->res = last_valid_res; + break; + } + if (test == check) + break; + + cval->res *= 2; + } +} + +/* * retrieve the minimum and maximum values for the specified control */ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, int default_min, struct snd_kcontrol *kctl) { - int i, idx; + int i, idx, ret; /* for failsafe */ cval->min = default_min; @@ -1257,7 +1331,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->head.id, mixer_ctrl_intf(cval->head.mixer), cval->control, cval->head.id); - return -EINVAL; + return -EAGAIN; } if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, @@ -1280,37 +1354,25 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, if (cval->res == 0) cval->res = 1; - /* Additional checks for the proper resolution - * - * Some devices report smaller resolutions than actually - * reacting. They don't return errors but simply clip - * to the lower aligned value. - */ - if (cval->min + cval->res < cval->max) { - int last_valid_res = cval->res; - int saved, test, check; + if (cval->min < cval->max) { + int saved; + if (get_cur_mix_raw(cval, minchn, &saved) < 0) - goto no_res_check; - for (;;) { - test = saved; - if (test < cval->max) - test += cval->res; - else - test -= cval->res; - if (test < cval->min || test > cval->max || - snd_usb_set_cur_mix_value(cval, minchn, 0, test) || - get_cur_mix_raw(cval, minchn, &check)) { - cval->res = last_valid_res; - break; - } - if (test == check) - break; - cval->res *= 2; + goto no_checks; + + ret = check_sticky_volume_control(cval, minchn, saved); + if (ret < 0) { + snd_usb_set_cur_mix_value(cval, minchn, 0, saved); + return ret; } + + if (cval->min + cval->res < cval->max) + check_volume_control_res(cval, minchn, saved); + snd_usb_set_cur_mix_value(cval, minchn, 0, saved); } -no_res_check: +no_checks: cval->initialized = 1; } @@ -1381,6 +1443,7 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol); + int ret; if (cval->val_type == USB_MIXER_BOOLEAN || cval->val_type == USB_MIXER_INV_BOOLEAN) @@ -1391,8 +1454,9 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, if (cval->val_type != USB_MIXER_BOOLEAN && cval->val_type != USB_MIXER_INV_BOOLEAN) { if (!cval->initialized) { - get_min_max_with_quirks(cval, 0, kcontrol); - if (cval->initialized && cval->dBmin >= cval->dBmax) { + ret = get_min_max_with_quirks(cval, 0, kcontrol); + if ((ret >= 0 || ret == -EAGAIN) && + cval->initialized && cval->dBmin >= cval->dBmax) { kcontrol->vd[0].access &= ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); @@ -1660,9 +1724,72 @@ static const struct usb_feature_control_info *get_feature_control_info(int contr return NULL; } +static bool check_insane_volume_range(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl, + struct usb_mixer_elem_info *cval) +{ + int range, steps, threshold; + + /* + * If a device quirk has overrode our TLV callback, no warning should + * be generated since our checks are only meaningful for dB volume. + */ + if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) || + kctl->tlv.c != snd_usb_mixer_vol_tlv) + return false; + + /* + * Meaningless volume control capability (<1dB). This should cover + * devices mapping their volume to val = 0/100/1, which are very likely + * to be quirky. + */ + range = cval->max - cval->min; + if (range < 256) { + usb_audio_warn(mixer->chip, + "Warning! Unlikely small volume range (=%u), linear volume or custom curve?", + range); + return true; + } + + steps = range / cval->res; + + /* + * There are definitely devices with ~20,000 ranges (e.g., HyperX Cloud + * III with val = -18944/0/1), so we use some heuristics here: + * + * min < 0 < max: Attenuator + amplifier? Likely to be sane + * + * min < 0 = max: DSP? Voltage attenuator with FW conversion to dB? + * Likely to be sane + * + * min < max < 0: Measured values? Neutral + * + * min = 0 < max: Oversimplified FW conversion? Linear volume? Likely to + * be quirky (e.g., MV-SILICON) + * + * 0 < min < max: Amplifier with fixed gains? Likely to be quirky + * (e.g., Logitech webcam) + */ + if (cval->min < 0 && 0 <= cval->max) + threshold = 24576; /* 65535 * (3 / 8) */ + else if (cval->min < cval->max && cval->max < 0) + threshold = 1024; + else + threshold = 384; + + if (steps > threshold) { + usb_audio_warn(mixer->chip, + "Warning! Unlikely big volume step count (=%u), linear volume or wrong cval->res?", + steps); + return true; + } + + return false; +} + static void __build_feature_ctl(struct usb_mixer_interface *mixer, const struct usbmix_name_map *imap, - unsigned int ctl_mask, int control, + u64 ctl_mask, int control, struct usb_audio_term *iterm, struct usb_audio_term *oterm, int unitid, int nameid, int readonly_mask) @@ -1673,7 +1800,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; - unsigned int range; + int ret; if (control == UAC_FU_GRAPHIC_EQUALIZER) { /* FIXME: not supported yet */ @@ -1707,7 +1834,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, cval->master_readonly = readonly_mask; } else { int i, c = 0; - for (i = 0; i < 16; i++) + for (i = 0; i < MAX_CHANNELS; i++) if (ctl_mask & BIT(i)) c++; cval->channels = c; @@ -1787,10 +1914,10 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, } /* get min/max values */ - get_min_max_with_quirks(cval, 0, kctl); + ret = get_min_max_with_quirks(cval, 0, kctl); /* skip a bogus volume range */ - if (cval->max <= cval->min) { + if ((ret < 0 && ret != -EAGAIN) || cval->max <= cval->min) { usb_audio_dbg(mixer->chip, "[%d] FU [%s] skipped due to invalid volume\n", cval->head.id, kctl->id.name); @@ -1811,29 +1938,21 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl); - range = (cval->max - cval->min) / cval->res; - /* - * There are definitely devices with a range of ~20,000, so let's be - * conservative and allow for a bit more. - */ - if (range > 65535) { - usb_audio_warn(mixer->chip, - "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.", - range); - usb_audio_warn(mixer->chip, - "[%d] FU [%s] ch = %d, val = %d/%d/%d", + if (check_insane_volume_range(mixer, kctl, cval)) { + usb_audio_warn(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); + } else { + usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", + cval->head.id, kctl->id.name, cval->channels, + cval->min, cval->max, cval->res); } - usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", - cval->head.id, kctl->id.name, cval->channels, - cval->min, cval->max, cval->res); snd_usb_mixer_add_control(&cval->head, kctl); } static void build_feature_ctl(struct mixer_build *state, void *raw_desc, - unsigned int ctl_mask, int control, + u64 ctl_mask, int control, struct usb_audio_term *iterm, int unitid, int readonly_mask) { @@ -1845,7 +1964,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, } static void build_feature_ctl_badd(struct usb_mixer_interface *mixer, - unsigned int ctl_mask, int control, int unitid, + u64 ctl_mask, int control, int unitid, const struct usbmix_name_map *badd_map) { __build_feature_ctl(mixer, badd_map, ctl_mask, control, @@ -2021,7 +2140,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, bmaControls = ftr->bmaControls; } - if (channels > 32) { + if (channels > MAX_CHANNELS) { usb_audio_info(state->chip, "usbmixer: too many channels (%d) in unit %d\n", channels, unitid); @@ -2059,7 +2178,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, if (state->mixer->protocol == UAC_VERSION_1) { /* check all control types */ for (i = 0; i < 10; i++) { - unsigned int ch_bits = 0; + u64 ch_bits = 0; int control = audio_feature_info[i].control; for (j = 0; j < channels; j++) { @@ -2085,7 +2204,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, } } else { /* UAC_VERSION_2/3 */ for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) { - unsigned int ch_bits = 0; + u64 ch_bits = 0; unsigned int ch_read_only = 0; int control = audio_feature_info[i].control; @@ -2172,6 +2291,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned int i, len; struct snd_kcontrol *kctl; const struct usbmix_name_map *map; + int ret; map = find_map(state->map, unitid, 0); if (check_ignored_ctl(map)) @@ -2194,7 +2314,11 @@ static void build_mixer_unit_ctl(struct mixer_build *state, } /* get min/max values */ - get_min_max(cval, 0); + ret = get_min_max(cval, 0); + if (ret < 0 && ret != -EAGAIN) { + usb_mixer_elem_info_free(cval); + return; + } kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (!kctl) { @@ -2563,10 +2687,11 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, cval->max = control_spec[0]; cval->res = 1; cval->initialized = 1; + err = 0; break; } - get_min_max(cval, valinfo->min_value); + err = get_min_max(cval, valinfo->min_value); break; } case USB_XU_CLOCK_RATE: @@ -2578,11 +2703,16 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, cval->max = 5; cval->res = 1; cval->initialized = 1; + err = 0; break; default: - get_min_max(cval, valinfo->min_value); + err = get_min_max(cval, valinfo->min_value); break; } + if (err < 0 && err != -EAGAIN) { + usb_mixer_elem_info_free(cval); + return err; + } err = get_cur_ctl_value(cval, cval->control << 8, &val); if (err < 0) { @@ -3398,7 +3528,7 @@ static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, [USB_MIXER_U32] = "U32", [USB_MIXER_BESPOKEN] = "BESPOKEN", }; - snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " + snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%llx, " "channels=%i, type=\"%s\"\n", cval->head.id, cval->control, cval->cmask, cval->channels, val_types[cval->val_type]); diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 167fbfcf01ac..afbb3dd9f177 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -44,7 +44,7 @@ struct usb_mixer_interface { void (*private_suspend)(struct usb_mixer_interface *mixer); }; -#define MAX_CHANNELS 16 /* max logical channels */ +#define MAX_CHANNELS 64 /* max logical channels */ enum { USB_MIXER_BOOLEAN, @@ -81,7 +81,7 @@ struct usb_mixer_elem_list { struct usb_mixer_elem_info { struct usb_mixer_elem_list head; unsigned int control; /* CS or ICN (high byte) */ - unsigned int cmask; /* channel mask bitmap: 0 = master */ + u64 cmask; /* channel mask bitmap: 0 = master */ unsigned int idx_off; /* Control index offset */ unsigned int ch_readonly; unsigned int master_readonly; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 11e205da7964..a01510a855c2 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -4477,6 +4477,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x194f, 0x010d): /* Presonus Studio 1824c */ err = snd_sc1810_init_mixer(mixer); break; + case USB_ID(0x194f, 0x0107): /* Presonus Studio 1824 */ + err = snd_sc1810_init_mixer(mixer); + break; case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */ err = snd_bbfpro_controls_create(mixer); break; @@ -4588,6 +4591,24 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer, } } +static void snd_usb_mv_silicon_quirks(struct usb_mixer_interface *mixer, + struct usb_mixer_elem_info *cval, + struct snd_kcontrol *kctl) +{ + if (cval->min == 0 && cval->max == 4096 && cval->res == 1) { + /* The final effects will be printed later. */ + usb_audio_info(mixer->chip, "applying MV-SILICON quirks (0/4096/1 variant)\n"); + + /* Respect MIN_MUTE set by module parameters. */ + if (!(mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE)) + mixer->chip->quirk_flags |= QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL; + if (!(mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE)) + mixer->chip->quirk_flags |= QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL; + } else { + usb_audio_dbg(mixer->chip, "not applying MV-SILICON quirks on unknown variant"); + } +} + /* * Some Plantronics headsets have control names that don't meet ALSA naming * standards. This function fixes nonstandard source names. By the time @@ -4634,6 +4655,25 @@ triggered: usb_audio_dbg(chip, "something wrong in kctl name %s\n", id->name); } +static void snd_usb_mixer_fu_quirk_linear_scale(struct usb_mixer_interface *mixer, + struct usb_mixer_elem_info *cval, + struct snd_kcontrol *kctl) +{ + static const DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0); + + if (cval->min_mute) { + /* + * We are clearing SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, + * resulting in min_mute being a no-op. + */ + usb_audio_warn(mixer->chip, "LINEAR_VOL overrides MIN_MUTE\n"); + } + + kctl->tlv.p = scale; + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; +} + void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, struct usb_mixer_elem_info *cval, int unitid, struct snd_kcontrol *kctl) @@ -4645,6 +4685,10 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, break; } + if (cval->control == UAC_FU_VOLUME && + !strncmp(mixer->chip->card->longname, "MV-SILICON", 10)) + snd_usb_mv_silicon_quirks(mixer, cval, kctl); + /* lowest playback value is muted on some devices */ if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE) if (strstr(kctl->id.name, "Playback")) { @@ -4660,6 +4704,21 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, "applying capture min mute quirk\n"); cval->min_mute = 1; } + + if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL) + if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Playback")) { + usb_audio_info(mixer->chip, + "applying playback linear volume quirk\n"); + snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl); + } + + if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL) + if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Capture")) { + usb_audio_info(mixer->chip, + "applying capture linear volume quirk\n"); + snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl); + } + /* ALSA-ify some Plantronics headset control names */ if (USB_ID_VENDOR(mixer->chip->usb_id) == 0x047f && (cval->control == UAC_FU_MUTE || cval->control == UAC_FU_VOLUME)) diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c index 7eac7d1bce64..2e5a8d37ec57 100644 --- a/sound/usb/mixer_s1810c.c +++ b/sound/usb/mixer_s1810c.c @@ -362,6 +362,7 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, MIXER_LEVEL_0DB); break; + case USB_ID(0x194f, 0x0107): /* 1824 */ case USB_ID(0x194f, 0x010d): /* 1824c */ /* Set all output faders to unity gain */ a = SC1810C_SEL_OUTPUT; @@ -685,6 +686,7 @@ int snd_sc1810_init_mixer(struct usb_mixer_interface *mixer) return ret; break; + case USB_ID(0x194f, 0x0107): /* Presonus Studio 1824 */ case USB_ID(0x194f, 0x010d): /* Presonus Studio 1824c */ ret = snd_s1810c_switch_init(mixer, &snd_s1824c_mono_sw); if (ret < 0) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index fd1fb668929a..8eaa96222759 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -2262,7 +2262,7 @@ static const struct scarlett2_device_entry scarlett2_devices[] = { { USB_ID(0x1235, 0x820c), &clarett_8pre_info, "Clarett+" }, /* End of list */ - { 0, NULL }, + { 0, NULL, NULL }, }; /* get the starting port index number for a given port type/direction */ diff --git a/sound/usb/qcom/Makefile b/sound/usb/qcom/Makefile index 6567727b66f0..aa8fcf8d2458 100644 --- a/sound/usb/qcom/Makefile +++ b/sound/usb/qcom/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-usb-audio-qmi-y := usb_audio_qmi_v01.o qc_audio_offload.o snd-usb-audio-qmi-y += mixer_usb_offload.o obj-$(CONFIG_SND_USB_AUDIO_QMI) += snd-usb-audio-qmi.o diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 2ac813d57f4f..5f993b88448c 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -948,7 +948,7 @@ static int enable_audio_stream(struct snd_usb_substream *subs, _snd_pcm_hw_params_any(¶ms); m = hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT); - snd_mask_leave(m, pcm_format); + snd_mask_leave(m, (__force unsigned int)pcm_format); i = hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS); snd_interval_setinteger(i); diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index eafc0d73cca1..803e03d4d77b 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2652,6 +2652,54 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + /* + * The AudioBox USB advertises S24_3LE as the only supported format + * for both playback and capture. It does not support S16_LE despite + * being a USB full-speed device. + */ + USB_DEVICE(0x194f, 0x0301), + QUIRK_DRIVER_INFO { + .vendor_name = "PreSonus", + .product_name = "AudioBox USB", + QUIRK_DATA_COMPOSITE { + { QUIRK_DATA_IGNORE(0) }, + { + QUIRK_DATA_AUDIOFORMAT(2) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 2, + .iface = 2, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + } + }, + { + QUIRK_DATA_AUDIOFORMAT(3) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 2, + .iface = 3, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x82, + .ep_attr = USB_ENDPOINT_XFER_ISOC, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + } + }, + QUIRK_COMPOSITE_END + } + } +}, #endif /* disabled */ { @@ -3900,5 +3948,70 @@ YAMAHA_DEVICE(0x7010, "UB99"), QUIRK_RME_DIGIFACE(0x3f8c), QUIRK_RME_DIGIFACE(0x3fa0), +#define QUIRK_AF16RIG(channel_count_, alt_setting_, \ + low_rate_, high_rate_, pack_size_, \ + clock_, interface_, endpoint_) \ + { \ + QUIRK_DATA_AUDIOFORMAT(interface_) { \ + .formats = SNDRV_PCM_FMTBIT_S32_LE, \ + .channels = channel_count_, \ + .fmt_type = UAC_FORMAT_TYPE_I_PCM, \ + .fmt_bits = 24, \ + .fmt_sz = 4, \ + .iface = interface_, \ + .altsetting = alt_setting_, \ + .altset_idx = alt_setting_, \ + .endpoint = endpoint_, \ + .ep_attr = USB_ENDPOINT_XFER_ISOC | \ + USB_ENDPOINT_SYNC_ASYNC, \ + .datainterval = 1, \ + .protocol = UAC_VERSION_2, \ + .maxpacksize = pack_size_, \ + .rates = SNDRV_PCM_RATE_##low_rate_ | \ + SNDRV_PCM_RATE_##high_rate_, \ + .rate_min = low_rate_, \ + .rate_max = high_rate_, \ + .nr_rates = 2, \ + .rate_table = (unsigned int[]) { \ + low_rate_, high_rate_ }, \ + .clock = clock_, \ + } \ + } + +#define QUIRK_AF16RIG_CLOCK(clock) \ + QUIRK_AF16RIG(34, 1, 44100, 48000, 0x3b8, clock, 1, 0x01), \ + QUIRK_AF16RIG(34, 1, 44100, 48000, 0x3b8, clock, 2, 0x81), \ + QUIRK_AF16RIG(18, 2, 88200, 96000, 0x3a8, clock, 1, 0x01), \ + QUIRK_AF16RIG(18, 2, 88200, 96000, 0x3a8, clock, 2, 0x81), \ + QUIRK_AF16RIG(10, 3, 176400, 192000, 0x3e8, clock, 1, 0x01), \ + QUIRK_AF16RIG(10, 3, 176400, 192000, 0x3e8, clock, 2, 0x81) + +/* Arturia AudioFuse 16Rig Audio */ +/* AF16Rig MIDI has USB PID 0xaf21 and appears to work OK without quirks */ +{ + USB_DEVICE(0x1c75, 0xaf20), + QUIRK_DRIVER_INFO { + .vendor_name = "Arturia", + .product_name = "AF16Rig", + QUIRK_DATA_COMPOSITE { + { QUIRK_DATA_STANDARD_MIXER(0) }, + QUIRK_AF16RIG_CLOCK(41), /* Internal clock */ +#if 0 +/* These are disabled because I don't have the required hardware to test + * them. I suspect that the ADAT clock might not follow 176400 or 192000 + * because the AF16Rig won't accept ADAT audio data at those rates. + */ + QUIRK_AF16RIG_CLOCK(43), /* ADAT clock */ + QUIRK_AF16RIG_CLOCK(44), /* BNC word clock */ +#endif + { QUIRK_DATA_IGNORE(3) }, /* Firmware update */ + QUIRK_COMPOSITE_END + } + } +}, + +#undef QUIRK_AF16RIG_CLOCK +#undef QUIRK_AF16RIG + #undef USB_DEVICE_VENDOR_SPEC #undef USB_AUDIO_DEVICE diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 4cfa24c06fcd..8fc36d1cfb9d 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2,8 +2,11 @@ /* */ +#include <linux/cleanup.h> +#include <linux/err.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/midi.h> @@ -2135,16 +2138,69 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, /* * driver behavior quirk flags */ +struct usb_string_match { + const char *manufacturer; + const char *product; +}; + struct usb_audio_quirk_flags_table { u32 id; u32 flags; + const struct usb_string_match *usb_string_match; }; #define DEVICE_FLG(vid, pid, _flags) \ { .id = USB_ID(vid, pid), .flags = (_flags) } #define VENDOR_FLG(vid, _flags) DEVICE_FLG(vid, 0, _flags) +/* + * Use as a last resort if using DEVICE_FLG() is prone to VID/PID conflicts. + * + * Usage: + * // match vid, pid, "manufacturer", and "product" + * DEVICE_STRING_FLG(vid, pid, "manufacturer", "product", flags) + * + * // match vid, pid, "manufacturer", and any product string + * DEVICE_STRING_FLG(vid, pid, "manufacturer", NULL, flags) + * + * // match vid, pid, "manufacturer", and device must have no product string + * DEVICE_STRING_FLG(vid, pid, "manufacturer", "", flags) + * + * // match vid, pid, any manufacturer string, and "product" + * DEVICE_STRING_FLG(vid, pid, NULL, "product", flags) + * + * // match vid, pid, no manufacturer string, and "product" + * DEVICE_STRING_FLG(vid, pid, "", "product", flags) + * + * // match vid, pid, no manufacturer string, and no product string + * DEVICE_STRING_FLG(vid, pid, "", "", flags) + */ +#define DEVICE_STRING_FLG(vid, pid, _manufacturer, _product, _flags) \ +{ \ + .id = USB_ID(vid, pid), \ + .usb_string_match = &(const struct usb_string_match) { \ + .manufacturer = _manufacturer, \ + .product = _product, \ + }, \ + .flags = (_flags), \ +} + +/* + * Use as a last resort if using VENDOR_FLG() is prone to VID conflicts. + * + * Usage: + * // match vid, and "manufacturer" + * VENDOR_STRING_FLG(vid, "manufacturer", flags) + * + * // match vid, and device must have no manufacturer string + * VENDOR_STRING_FLG(vid, "", flags) + */ +#define VENDOR_STRING_FLG(vid, _manufacturer, _flags) \ + DEVICE_STRING_FLG(vid, 0, _manufacturer, NULL, _flags) + static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + /* Device and string descriptor matches */ + /* Device matches */ DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), @@ -2281,6 +2337,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0d8c, 0x0014, /* C-Media */ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */ + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */ @@ -2291,8 +2349,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */ QUIRK_FLAG_GET_SAMPLE_RATE), - DEVICE_FLG(0x12d1, 0x3a07, /* Huawei Technologies Co., Ltd. */ - QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE | QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE), + DEVICE_FLG(0x12d1, 0x3a07, /* HUAWEI USB-C HEADSET */ + QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE | QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE | + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */ @@ -2421,6 +2480,13 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */ QUIRK_FLAG_ALIGN_TRANSFER), + DEVICE_FLG(0x84ef, 0x0082, /* Hotone Audio Pulze Mini */ + QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL | QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL), + + /* Vendor and string descriptor matches */ + VENDOR_STRING_FLG(0x1235, /* Conflict with Focusrite Novation */ + "MV-SILICON", + 0), /* Stop matching */ /* Vendor matches */ VENDOR_FLG(0x045e, /* MS Lifecam */ @@ -2435,6 +2501,7 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_VALIDATE_RATES), DEVICE_FLG(0x1235, 0x8006, 0), /* Focusrite Scarlett 2i2 1st Gen */ DEVICE_FLG(0x1235, 0x800a, 0), /* Focusrite Scarlett 2i4 1st Gen */ + DEVICE_FLG(0x1235, 0x800c, 0), /* Focusrite Scarlett 18i20 1st Gen */ DEVICE_FLG(0x1235, 0x8016, 0), /* Focusrite Scarlett 2i2 1st Gen */ DEVICE_FLG(0x1235, 0x801c, 0), /* Focusrite Scarlett Solo 1st Gen */ VENDOR_FLG(0x1235, /* Focusrite Novation */ @@ -2522,6 +2589,8 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { QUIRK_STRING_ENTRY(MIXER_PLAYBACK_MIN_MUTE), QUIRK_STRING_ENTRY(MIXER_CAPTURE_MIN_MUTE), QUIRK_STRING_ENTRY(SKIP_IFACE_SETUP), + QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL), + QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL), NULL }; @@ -2578,6 +2647,16 @@ void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip) if (chip->usb_id == p->id || (!USB_ID_PRODUCT(p->id) && USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) { + /* Handle DEVICE_STRING_FLG/VENDOR_STRING_FLG. */ + if (p->usb_string_match && p->usb_string_match->manufacturer && + strcmp(p->usb_string_match->manufacturer, + chip->dev->manufacturer ? chip->dev->manufacturer : "")) + continue; + if (p->usb_string_match && p->usb_string_match->product && + strcmp(p->usb_string_match->product, + chip->dev->product ? chip->dev->product : "")) + continue; + snd_usb_apply_flag_dbg("builtin table", chip, p->flags); chip->quirk_flags |= p->flags; return; diff --git a/sound/usb/stream.c b/sound/usb/stream.c index d38c39e28f38..2532bf97e05e 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -366,6 +366,8 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor /* * TODO: this conversion is not complete, update it * after adding UAC3 values to asound.h + * NOTE: not all UAC3 channel relationship have a + * direct ALSA chmap equivalent. */ switch (is->bChRelationship) { case UAC3_CH_MONO: @@ -390,6 +392,12 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor case UAC3_CH_FRONT_RIGHT_OF_CENTER: map = SNDRV_CHMAP_FRC; break; + case UAC3_CH_FRONT_WIDE_LEFT: + map = SNDRV_CHMAP_FLW; + break; + case UAC3_CH_FRONT_WIDE_RIGHT: + map = SNDRV_CHMAP_FRW; + break; case UAC3_CH_SIDE_LEFT: map = SNDRV_CHMAP_SL; break; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 085530cf62d9..58fd07f8c3c9 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -228,6 +228,14 @@ extern bool snd_usb_skip_validation; * Skip the probe-time interface setup (usb_set_interface, * init_pitch, init_sample_rate); redundant with * snd_usb_endpoint_prepare() at stream-open time + * QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL + * Set linear volume mapping for devices where the playback volume control + * value is mapped to voltage (instead of dB) level linearly. In short: + * x(raw) = (raw - raw_min) / (raw_max - raw_min); V(x) = k * x; + * dB(x) = 20 * log10(x). Overrides QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE + * QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL + * Similar to QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL, but for capture streams. + * Overrides QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE */ enum { @@ -258,6 +266,8 @@ enum { QUIRK_TYPE_MIXER_PLAYBACK_MIN_MUTE = 24, QUIRK_TYPE_MIXER_CAPTURE_MIN_MUTE = 25, QUIRK_TYPE_SKIP_IFACE_SETUP = 26, + QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27, + QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28, /* Please also edit snd_usb_audio_quirk_flag_names */ }; @@ -290,5 +300,7 @@ enum { #define QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE QUIRK_FLAG(MIXER_PLAYBACK_MIN_MUTE) #define QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE QUIRK_FLAG(MIXER_CAPTURE_MIN_MUTE) #define QUIRK_FLAG_SKIP_IFACE_SETUP QUIRK_FLAG(SKIP_IFACE_SETUP) +#define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL) +#define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL) #endif /* __USBAUDIO_H */ diff --git a/sound/usb/usx2y/us144mkii.c b/sound/usb/usx2y/us144mkii.c index 0cf4fa74e210..94553b61013c 100644 --- a/sound/usb/usx2y/us144mkii.c +++ b/sound/usb/usx2y/us144mkii.c @@ -420,7 +420,11 @@ static int tascam_probe(struct usb_interface *intf, /* The device has two interfaces; we drive both from this driver. */ if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { - tascam = usb_get_intfdata(usb_ifnum_to_if(dev, 0)); + struct usb_interface *intf_zero = usb_ifnum_to_if(dev, 0); + + if (!intf_zero) + return -ENODEV; + tascam = usb_get_intfdata(intf_zero); if (tascam) { usb_set_intfdata(intf, tascam); tascam->iface1 = intf; diff --git a/sound/x86/intel_hdmi_audio.h b/sound/x86/intel_hdmi_audio.h index 7ce8c2a7d714..88bf9ef2c491 100644 --- a/sound/x86/intel_hdmi_audio.h +++ b/sound/x86/intel_hdmi_audio.h @@ -1,30 +1,10 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright (C) 2016 Intel Corporation * Authors: Sailaja Bandarupalli <sailaja.bandarupalli@intel.com> * Ramesh Babu K V <ramesh.babu@intel.com> * Vaibhav Agarwal <vaibhav.agarwal@intel.com> * Jerome Anand <jerome.anand@intel.com> - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _INTEL_HDMI_AUDIO_H_ diff --git a/tools/testing/selftests/alsa/utimer-test.c b/tools/testing/selftests/alsa/utimer-test.c index d221972cd8fb..1a9ff010cb11 100644 --- a/tools/testing/selftests/alsa/utimer-test.c +++ b/tools/testing/selftests/alsa/utimer-test.c @@ -15,6 +15,7 @@ #include <stdlib.h> #include <pthread.h> #include <string.h> +#include <errno.h> #define FRAME_RATE 8000 #define PERIOD_SIZE 4410 @@ -52,7 +53,14 @@ FIXTURE_SETUP(timer_f) { timer_dev_fd = open("/dev/snd/timer", O_RDONLY); ASSERT_GE(timer_dev_fd, 0); - ASSERT_EQ(ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, self->utimer_info), 0); + if (ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, self->utimer_info) < 0) { + int err = errno; + + close(timer_dev_fd); + if (err == ENOTTY || err == ENXIO) + SKIP(return, "CONFIG_SND_UTIMER not enabled"); + ASSERT_EQ(err, 0); + } ASSERT_GE(self->utimer_info->fd, 0); close(timer_dev_fd); |
