diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-14 14:55:38 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-14 14:55:38 +0100 |
commit | 3ceff4ea07410763d5d4cccd60349bf7691e7e61 (patch) | |
tree | 1f7de4dac3f925cd44cf2eecd5feecabf71228c8 | |
parent | e1a7aa25ff45636a6c1930bf2430c8b802e93d9c (diff) | |
parent | 081c73701ef0c2a4f6a127da824a641ae6505fbe (diff) | |
download | linux-rpi-3ceff4ea07410763d5d4cccd60349bf7691e7e61.tar.gz linux-rpi-3ceff4ea07410763d5d4cccd60349bf7691e7e61.tar.bz2 linux-rpi-3ceff4ea07410763d5d4cccd60349bf7691e7e61.zip |
Merge tag 'sound-5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"It's a relatively calm development cycle, but still lots of updates in
the driver side like Intel SOF. Below are some highlights:
ALSA / ASoC core:
- A new kselftest for ALSA control API
- PCM NO_REWINDS support
- Potential race fixes around control removals
- Unify x86 SG-buffer memory allocation code
- Cleanups and race fixes for ASoC DPCM locking
ASoC:
- Refinements and cleanups around the delay() APIs
- Wider use of dev_err_probe().
- Continuing cleanups and improvements to the SOF code
- Support for pin switches in simple-card derived cards
- Support for AMD Renoir ACP, Asahi Kasei Microdevices AKM4375, Intel
systems using NAU8825 and MAX98390, Mediatek MT8915, nVidia Tegra20
S/PDIF, Qualcomm systems using ALC5682I-VS and Texas Instruments
TLV320ADC3xxx
HD-audio / USB-audio:
- Fix deadlock at HD-audio codec unbinding
- Fixes for Tegra194 HD-audio, new HDA support for CS35L41 codec
- Quirks for Lenovo and HP machines, Gigabyte mobo, Bose device
Misc:
- Fix virmidi drain behavior
Note that the merge of CS35L41 codec support is still half-baked, and
at least one ACPI change is missing. Although this won't hinder the
kernel build itself, we're going to catch up before RC1"
* tag 'sound-5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (415 commits)
ALSA: hda: intel-dsp-config: reorder the config table
ALSA: hda: intel-dsp-config: add JasperLake support
ALSA: hda: cs35l41: fix double free on error in probe()
ALSA: hda: Fix dependencies of CS35L41 on SPI/I2C buses
ALSA: hda: Fix dependency on ASoC cs35l41 codec
ASoC: cs35l41: Add support for hibernate memory retention mode
ASoC: cs35l41: Update handling of test key registers
ALSA: intel_hdmi: Check for error num after setting mask
ASoC: wcd9335: Keep a RX port value for each SLIM RX mux
ASoC: amd: acp: acp-mach: Change default RT1019 amp dev id
ALSA: virmidi: Remove duplicated code
ALSA: seq: virmidi: Add a drain operation
ASoC: topology: Fix typo
ASoC: fsl_asrc: refine the check of available clock divider
ASoC: Intel: bytcr_rt5640: Add support for external GPIO jack-detect
ASoC: Intel: bytcr_rt5640: Support retrieving the codec IRQ from the AMCR0F28 ACPI dev
ASoC: rt5640: Add support for boards with an external jack-detect GPIO
ASoC: rt5640: Allow snd_soc_component_set_jack() to override the codec IRQ
ASoC: rt5640: Change jack_work to a delayed_work
ASoC: rt5640: Fix possible NULL pointer deref on resume
...
414 files changed, 17555 insertions, 5674 deletions
diff --git a/Documentation/devicetree/bindings/sound/ak4375.yaml b/Documentation/devicetree/bindings/sound/ak4375.yaml new file mode 100644 index 000000000000..f1d5074a024d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4375.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ak4375.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4375 DAC and headphones amplifier Device Tree Bindings + +maintainers: + - Vincent Knecht <vincent.knecht@mailoo.org> + +properties: + compatible: + const: asahi-kasei,ak4375 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + avdd-supply: + description: regulator phandle for the AVDD power supply. + + tvdd-supply: + description: regulator phandle for the TVDD power supply. + + pdn-gpios: + description: optional GPIO to set the PDN pin. + +required: + - compatible + - reg + - '#sound-dai-cells' + - avdd-supply + - tvdd-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + headphones: audio-codec@10 { + compatible = "asahi-kasei,ak4375"; + reg = <0x10>; + avdd-supply = <®_headphones_avdd>; + tvdd-supply = <&pm8916_l6>; + pdn-gpios = <&msmgpio 114 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&headphones_pdn_default>; + #sound-dai-cells = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml b/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml index f50558ed914f..0705f91199a0 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml @@ -9,6 +9,9 @@ title: Amlogic AIU audio output controller maintainers: - Jerome Brunet <jbrunet@baylibre.com> +allOf: + - $ref: name-prefix.yaml# + properties: $nodename: pattern: "^audio-controller@.*" @@ -65,6 +68,8 @@ properties: resets: maxItems: 1 + sound-name-prefix: true + required: - "#sound-dai-cells" - compatible diff --git a/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml b/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml index 3c3891d17238..77469a45bb7a 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml @@ -9,6 +9,9 @@ title: Amlogic G12a Internal DAC Control Glue maintainers: - Jerome Brunet <jbrunet@baylibre.com> +allOf: + - $ref: name-prefix.yaml# + properties: $nodename: pattern: "^audio-controller@.*" @@ -31,6 +34,8 @@ properties: resets: maxItems: 1 + sound-name-prefix: true + required: - "#sound-dai-cells" - compatible diff --git a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml index db7b04da0b39..580a3d040abc 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml @@ -9,6 +9,9 @@ title: Amlogic T9015 Internal Audio DAC maintainers: - Jerome Brunet <jbrunet@baylibre.com> +allOf: + - $ref: name-prefix.yaml# + properties: $nodename: pattern: "^audio-controller@.*" @@ -38,6 +41,8 @@ properties: description: Analogue power supply. + sound-name-prefix: true + required: - "#sound-dai-cells" - compatible diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml index 43e7f86e3b23..476dcb49ece6 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml @@ -42,10 +42,15 @@ patternProperties: $ref: /schemas/types.yaml#/definitions/flag frame-master: description: Indicates dai-link frame master. - $ref: /schemas/types.yaml#/definitions/phandle + oneOf: + - $ref: /schemas/types.yaml#/definitions/flag + - $ref: /schemas/types.yaml#/definitions/phandle bitclock-master: description: Indicates dai-link bit clock master - $ref: /schemas/types.yaml#/definitions/phandle + oneOf: + - $ref: /schemas/types.yaml#/definitions/flag + - $ref: /schemas/types.yaml#/definitions/phandle + dai-format: description: audio format. items: diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml new file mode 100644 index 000000000000..31800f70e9d9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml @@ -0,0 +1,225 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs42l42.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS42L42 audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +description: + The CS42L42 is a low-power audio codec designed for portable applications. + It provides a high-dynamic range, stereo DAC for audio playback and a mono + high-dynamic-range ADC for audio capture. There is an integrated headset + detection block. + +properties: + compatible: + enum: + - cirrus,cs42l42 + + reg: + description: + The I2C address of the CS42L42. + maxItems: 1 + + VP-supply: + description: + VP power supply. + + VCP-supply: + description: + Charge pump power supply. + + VD_FILT-supply: + description: + FILT+ power supply. + + VL-supply: + description: + Logic power supply. + + VA-supply: + description: + Analog power supply. + + reset-gpios: + description: + This pin will be asserted and then deasserted to reset the + CS42L42 before communication starts. + maxItems: 1 + + interrupts: + description: + Interrupt for CS42L42 IRQ line. + maxItems: 1 + + cirrus,ts-inv: + description: | + Sets the behaviour of the jack plug detect switch. + + 0 - (Default) Shorted to tip when unplugged, open when plugged. + This is "inverted tip sense (ITS)" in the datasheet. + + 1 - Open when unplugged, shorted to tip when plugged. + This is "normal tip sense (TS)" in the datasheet. + + The CS42L42_TS_INV_* defines are available for this. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 1 + + cirrus,ts-dbnc-rise: + description: | + Debounce the rising edge of TIP_SENSE_PLUG. With no + debounce, the tip sense pin might be noisy on a plug event. + + 0 - 0ms + 1 - 125ms + 2 - 250ms + 3 - 500ms + 4 - 750ms + 5 - 1s (Default) + 6 - 1.25s + 7 - 1.5s + + The CS42L42_TS_DBNCE_* defines are available for this. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 7 + + cirrus,ts-dbnc-fall: + description: | + Debounce the falling edge of TIP_SENSE_UNPLUG. With no + debounce, the tip sense pin might be noisy on an unplug event. + + 0 - 0ms + 1 - 125ms + 2 - 250ms + 3 - 500ms + 4 - 750ms + 5 - 1s (Default) + 6 - 1.25s + 7 - 1.5s + + The CS42L42_TS_DBNCE_* defines are available for this. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 7 + + cirrus,btn-det-init-dbnce: + description: | + This sets how long to wait after enabling button detection + interrupts before servicing button interrupts, to allow the + HS bias time to settle. Value is in milliseconds. + There may be erroneous button interrupts if this debounce time + is too short. + + 0ms - 200ms, + Default = 100ms + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 200 + + cirrus,btn-det-event-dbnce: + description: | + This sets how long to wait after receiving a button press + interrupt before processing it. Allows time for the button + press to make a clean connection with the bias resistors. + Value is in milliseconds. + + 0ms - 20ms, + Default = 10ms + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 20 + + cirrus,bias-lvls: + description: | + For a level-detect headset button scheme, each button will bias + the mic pin to a certain voltage. To determine which button was + pressed, the voltage is compared to sequential, decreasing + voltages, until the compared voltage < bias voltage. + For different hardware setups, a designer might want to tweak this. + This is an array of descending values for the comparator voltage, + given as percent of the HSBIAS voltage. + + Array of 4 values, each 0-63 + < x1 x2 x3 x4 > + Default = < 15 8 4 1 > + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 4 + maxItems: 4 + items: + minimum: 0 + maximum: 63 + + cirrus,hs-bias-ramp-rate: + description: | + If present this sets the rate that the HS bias should rise and fall. + The actual rise and fall times depend on external hardware (the + datasheet gives several rise and fall time examples). + + 0 - Fast rise time; slow, load-dependent fall time + 1 - Fast + 2 - Slow (default) + 3 - Slowest + + The CS42L42_HSBIAS_RAMP_* defines are available for this. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 3 + + cirrus,hs-bias-sense-disable: + description: | + If present the HSBIAS sense is disabled. Configures HSBIAS output + current sense through the external 2.21-k resistor. HSBIAS_SENSE + is a hardware feature to reduce the potential pop noise when the + headset plug is removed slowly. But on some platforms ESD voltage + will affect it causing plug detection to fail, especially with CTIA + headset type. For different hardware setups, a designer might want + to tweak default behavior. + type: boolean + +required: + - compatible + - reg + - VP-supply + - VCP-supply + - VD_FILT-supply + - VL-supply + - VA-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/sound/cs42l42.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + cs42l42: cs42l42@48 { + compatible = "cirrus,cs42l42"; + reg = <0x48>; + VA-supply = <&dummy_vreg>; + VP-supply = <&dummy_vreg>; + VCP-supply = <&dummy_vreg>; + VD_FILT-supply = <&dummy_vreg>; + VL-supply = <&dummy_vreg>; + + reset-gpios = <&axi_gpio_0 1 0>; + interrupt-parent = <&gpio0>; + interrupts = <55 8>; + + cirrus,ts-inv = <CS42L42_TS_INV_DIS>; + cirrus,ts-dbnc-rise = <CS42L42_TS_DBNCE_1000>; + cirrus,ts-dbnc-fall = <CS42L42_TS_DBNCE_0>; + cirrus,btn-det-init-dbnce = <100>; + cirrus,btn-det-event-dbnce = <10>; + cirrus,bias-lvls = <0x0F 0x08 0x04 0x01>; + cirrus,hs-bias-ramp-rate = <CS42L42_HSBIAS_RAMP_SLOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cs42l42.txt b/Documentation/devicetree/bindings/sound/cs42l42.txt deleted file mode 100644 index 3b7705623980..000000000000 --- a/Documentation/devicetree/bindings/sound/cs42l42.txt +++ /dev/null @@ -1,115 +0,0 @@ -CS42L42 audio CODEC - -Required properties: - - - compatible : "cirrus,cs42l42" - - - reg : the I2C address of the device for I2C. - - - VP-supply, VCP-supply, VD_FILT-supply, VL-supply, VA-supply : - power supplies for the device, as covered in - Documentation/devicetree/bindings/regulator/regulator.txt. - -Optional properties: - - - reset-gpios : a GPIO spec for the reset pin. If specified, it will be - deasserted before communication to the codec starts. - - - interrupts : IRQ line info CS42L42. - (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt - for further information relating to interrupt properties) - - - cirrus,ts-inv : Boolean property. Sets the behaviour of the jack plug - detect switch. - - 0 = (Default) Shorted to tip when unplugged, open when plugged. - This is "inverted tip sense (ITS)" in the datasheet. - - 1 = Open when unplugged, shorted to tip when plugged. - This is "normal tip sense (TS)" in the datasheet. - - - cirrus,ts-dbnc-rise : Debounce the rising edge of TIP_SENSE_PLUG. With no - debounce, the tip sense pin might be noisy on a plug event. - - 0 - 0ms, - 1 - 125ms, - 2 - 250ms, - 3 - 500ms, - 4 - 750ms, - 5 - (Default) 1s, - 6 - 1.25s, - 7 - 1.5s, - - - cirrus,ts-dbnc-fall : Debounce the falling edge of TIP_SENSE_UNPLUG. - With no debounce, the tip sense pin might be noisy on an unplug event. - - 0 - 0ms, - 1 - 125ms, - 2 - 250ms, - 3 - 500ms, - 4 - 750ms, - 5 - (Default) 1s, - 6 - 1.25s, - 7 - 1.5s, - - - cirrus,btn-det-init-dbnce : This sets how long the driver sleeps after - enabling button detection interrupts. After auto-detection and before - servicing button interrupts, the HS bias needs time to settle. If you - don't wait, there is possibility for erroneous button interrupt. - - 0ms - 200ms, - Default = 100ms - - - cirrus,btn-det-event-dbnce : This sets how long the driver delays after - receiving a button press interrupt. With level detect interrupts, you want - to wait a small amount of time to make sure the button press is making a - clean connection with the bias resistors. - - 0ms - 20ms, - Default = 10ms - - - cirrus,bias-lvls : For a level-detect headset button scheme, each button - will bias the mic pin to a certain voltage. To determine which button was - pressed, the driver will compare this biased voltage to sequential, - decreasing voltages and will stop when a comparator is tripped, - indicating a comparator voltage < bias voltage. This value represents a - percentage of the internally generated HS bias voltage. For different - hardware setups, a designer might want to tweak this. This is an array of - descending values for the comparator voltage. - - Array of 4 values - Each 0-63 - < x1 x2 x3 x4 > - Default = < 15 8 4 1> - - - cirrus,hs-bias-sense-disable: This is boolean property. If present the - HSBIAS sense is disabled. Configures HSBIAS output current sense through - the external 2.21-k resistor. HSBIAS_SENSE is hardware feature to reduce - the potential pop noise during the headset plug out slowly. But on some - platforms ESD voltage will affect it causing test to fail, especially - with CTIA headset type. For different hardware setups, a designer might - want to tweak default behavior. - -Example: - -cs42l42: cs42l42@48 { - compatible = "cirrus,cs42l42"; - reg = <0x48>; - VA-supply = <&dummy_vreg>; - VP-supply = <&dummy_vreg>; - VCP-supply = <&dummy_vreg>; - VD_FILT-supply = <&dummy_vreg>; - VL-supply = <&dummy_vreg>; - - reset-gpios = <&axi_gpio_0 1 0>; - interrupt-parent = <&gpio0>; - interrupts = <55 8> - - cirrus,ts-inv = <0x00>; - cirrus,ts-dbnc-rise = <0x05>; - cirrus,ts-dbnc-fall = <0x00>; - cirrus,btn-det-init-dbnce = <100>; - cirrus,btn-det-event-dbnce = <10>; - cirrus,bias-lvls = <0x0F 0x08 0x04 0x01>; - cirrus,hs-bias-ramp-rate = <0x02>; -}; diff --git a/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml b/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml index c6b070e1d014..a4f9257e313d 100644 --- a/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml +++ b/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml @@ -9,6 +9,9 @@ title: Dummy SPDIF Transmitter Device Tree Bindings maintainers: - Mark Brown <broonie@kernel.org> +allOf: + - $ref: name-prefix.yaml# + properties: compatible: const: linux,spdif-dit @@ -16,6 +19,8 @@ properties: "#sound-dai-cells": const: 0 + sound-name-prefix: true + required: - "#sound-dai-cells" - compatible diff --git a/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml index dcf790b053d2..6d0975b33d15 100644 --- a/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml @@ -19,6 +19,12 @@ properties: interrupts: maxItems: 1 + memory-region: + maxItems: 1 + description: | + Shared memory region for AFE memif. A "shared-dma-pool". + See ../reserved-memory/reserved-memory.txt for details. + mediatek,topckgen: $ref: "/schemas/types.yaml#/definitions/phandle" description: The phandle of the mediatek topckgen controller @@ -125,6 +131,7 @@ required: - power-domains - clocks - clock-names + - memory-region additionalProperties: false @@ -139,6 +146,7 @@ examples: interrupts = <GIC_SPI 822 IRQ_TYPE_LEVEL_HIGH 0>; mediatek,topckgen = <&topckgen>; power-domains = <&spm 7>; //MT8195_POWER_DOMAIN_AUDIO + memory-region = <&snd_dma_mem_reserved>; clocks = <&clk26m>, <&topckgen 163>, //CLK_TOP_APLL1 <&topckgen 166>, //CLK_TOP_APLL2 diff --git a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml index d354c30d3377..cf6ad7933e23 100644 --- a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml +++ b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml @@ -16,6 +16,10 @@ properties: compatible: const: mediatek,mt8195_mt6359_rt1011_rt5682 + model: + $ref: /schemas/types.yaml#/definitions/string + description: User specified audio sound card name + mediatek,platform: $ref: "/schemas/types.yaml#/definitions/phandle" description: The phandle of MT8195 ASoC platform. diff --git a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml index 20bc0ffd0e34..8f177e02ad35 100644 --- a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml +++ b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml @@ -16,6 +16,10 @@ properties: compatible: const: mediatek,mt8195_mt6359_rt1019_rt5682 + model: + $ref: /schemas/types.yaml#/definitions/string + description: User specified audio sound card name + mediatek,platform: $ref: "/schemas/types.yaml#/definitions/phandle" description: The phandle of MT8195 ASoC platform. @@ -28,6 +32,16 @@ properties: $ref: "/schemas/types.yaml#/definitions/phandle" description: The phandle of MT8195 HDMI codec node. + mediatek,adsp: + $ref: "/schemas/types.yaml#/definitions/phandle" + description: The phandle of MT8195 ADSP platform. + + mediatek,dai-link: + $ref: /schemas/types.yaml#/definitions/string-array + description: + A list of the desired dai-links in the sound card. Each entry is a + name defined in the machine driver. + additionalProperties: false required: diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt deleted file mode 100644 index 57f40f93453e..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt +++ /dev/null @@ -1,48 +0,0 @@ -NVIDIA Tegra audio complex - -Required properties: -- compatible : "nvidia,tegra-audio-alc5632" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,audio-routing : 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 ALC5632's pins as documented in the binding for the device - and: - - * Headset Stereophone - * Int Spk - * Headset Mic - * Digital Mic - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller -- nvidia,audio-codec : The phandle of the ALC5632 audio codec - -Example: - -sound { - compatible = "nvidia,tegra-audio-alc5632-paz00", - "nvidia,tegra-audio-alc5632"; - - nvidia,model = "Compal PAZ00"; - - nvidia,audio-routing = - "Int Spk", "SPK_OUTP", - "Int Spk", "SPK_OUTN", - "Headset Mic","MICBIAS1", - "MIC1_N", "Headset Mic", - "MIC1_P", "Headset Mic", - "Headset Stereophone", "HP_OUT_R", - "Headset Stereophone", "HP_OUT_L"; - - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&alc5632>; - - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.yaml new file mode 100644 index 000000000000..7ef774910e5c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-alc5632.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with ALC5632 CODEC + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-alc5632(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-alc5632 + + 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 + - "Headset Stereophone" + - "Int Spk" + - "Headset Mic" + - "Digital Mic" + + # CODEC Pins + - SPKOUT + - SPKOUTN + - MICBIAS1 + - MIC1 + - HPR + - HPL + - DMICDAT + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-alc5632-paz00", + "nvidia,tegra-audio-alc5632"; + + nvidia,model = "Compal PAZ00"; + + nvidia,audio-routing = "Int Spk", "SPKOUT", + "Int Spk", "SPKOUTN", + "Headset Mic", "MICBIAS1", + "MIC1", "Headset Mic", + "Headset Stereophone", "HPR", + "Headset Stereophone", "HPL", + "DMICDAT", "Digital Mic"; + + nvidia,i2s-controller = <&i2s>; + nvidia,audio-codec = <&codec>; + + clocks = <&clk 112>, <&clk 113>, <&clk 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml new file mode 100644 index 000000000000..82801b4f46dd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/sound/nvidia,tegra-audio-common.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Common properties for NVIDIA Tegra audio complexes + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +properties: + clocks: + items: + - description: PLL A clock + - description: PLL A OUT0 clock + - description: The Tegra cdev1/extern1 clock, which feeds the card's mclk + + clock-names: + items: + - const: pll_a + - const: pll_a_out0 + - const: mclk + + nvidia,model: + $ref: /schemas/types.yaml#/definitions/string + description: The user-visible name of this sound complex. + + 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. + + nvidia,ac97-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the AC97 controller + + nvidia,i2s-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the Tegra I2S controller + + nvidia,audio-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of audio codec + + nvidia,spkr-en-gpios: + maxItems: 1 + description: The GPIO that enables the speakers + + nvidia,hp-mute-gpios: + maxItems: 1 + description: The GPIO that mutes the headphones + + nvidia,hp-det-gpios: + maxItems: 1 + description: The GPIO that detect headphones are plugged in + + nvidia,mic-det-gpios: + maxItems: 1 + description: The GPIO that detect microphone is plugged in + + nvidia,ear-sel-gpios: + maxItems: 1 + description: The GPIO that switch between the microphones + + nvidia,int-mic-en-gpios: + maxItems: 1 + description: The GPIO that enables the internal microphone + + nvidia,ext-mic-en-gpios: + maxItems: 1 + description: The GPIO that enables the external microphone + + nvidia,headset: + type: boolean + description: The Mic Jack represents state of the headset microphone pin + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml index 5bdd30a8a404..b4bee466d67a 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml @@ -44,6 +44,16 @@ properties: minItems: 1 maxItems: 3 + interconnects: + items: + - description: APE read memory client + - description: APE write memory client + + interconnect-names: + items: + - const: dma-mem # read + - const: write + iommus: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt deleted file mode 100644 index c3495beba358..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt +++ /dev/null @@ -1,53 +0,0 @@ -NVIDIA Tegra audio complex, with MAX98090 CODEC - -Required properties: -- compatible : "nvidia,tegra-audio-max98090" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,audio-routing : 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 MAX98090's pins (as documented in its binding), and the jacks - on the board: - - * Headphones - * Speakers - * Mic Jack - * Int Mic - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's - connected to the CODEC. -- nvidia,audio-codec : The phandle of the MAX98090 audio codec. - -Optional properties: -- nvidia,hp-det-gpios : The GPIO that detect headphones are plugged in -- nvidia,mic-det-gpios : The GPIO that detect microphones are plugged in - -Example: - -sound { - compatible = "nvidia,tegra-audio-max98090-venice2", - "nvidia,tegra-audio-max98090"; - nvidia,model = "NVIDIA Tegra Venice2"; - - nvidia,audio-routing = - "Headphones", "HPR", - "Headphones", "HPL", - "Speakers", "SPKR", - "Speakers", "SPKL", - "Mic Jack", "MICBIAS", - "IN34", "Mic Jack"; - - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&acodec>; - - clocks = <&tegra_car TEGRA124_CLK_PLL_A>, - <&tegra_car TEGRA124_CLK_PLL_A_OUT0>, - <&tegra_car TEGRA124_CLK_EXTERN1>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.yaml new file mode 100644 index 000000000000..ccc2ee77ca30 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-max98090.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with MAX98090 CODEC + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + oneOf: + - items: + - pattern: '^[a-z0-9]+,tegra-audio-max98090(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-max98090 + - items: + - enum: + - nvidia,tegra-audio-max98090-nyan-big + - nvidia,tegra-audio-max98090-nyan-blaze + - const: nvidia,tegra-audio-max98090-nyan + - const: nvidia,tegra-audio-max98090 + + 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 + - "Headphones" + - "Speakers" + - "Mic Jack" + - "Int Mic" + + # CODEC Pins + - MIC1 + - MIC2 + - DMICL + - DMICR + - IN1 + - IN2 + - IN3 + - IN4 + - IN5 + - IN6 + - IN12 + - IN34 + - IN56 + - HPL + - HPR + - SPKL + - SPKR + - RCVL + - RCVR + - MICBIAS + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/tegra124-car.h> + + sound { + compatible = "nvidia,tegra-audio-max98090-venice2", + "nvidia,tegra-audio-max98090"; + nvidia,model = "NVIDIA Tegra Venice2"; + + nvidia,audio-routing = + "Headphones", "HPR", + "Headphones", "HPL", + "Speakers", "SPKR", + "Speakers", "SPKL", + "Mic Jack", "MICBIAS", + "IN34", "Mic Jack"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&acodec>; + + clocks = <&tegra_car TEGRA124_CLK_PLL_A>, + <&tegra_car TEGRA124_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA124_CLK_EXTERN1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt deleted file mode 100644 index 7788808dcd0b..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt +++ /dev/null @@ -1,52 +0,0 @@ -NVIDIA Tegra audio complex, with RT5640 CODEC - -Required properties: -- compatible : "nvidia,tegra-audio-rt5640" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,audio-routing : 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 RT5640's pins (as documented in its binding), and the jacks - on the board: - - * Headphones - * Speakers - * Mic Jack - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's - connected to the CODEC. -- nvidia,audio-codec : The phandle of the RT5640 audio codec. This binding - assumes that AIF1 on the CODEC is connected to Tegra. - -Optional properties: -- nvidia,hp-det-gpios : The GPIO that detects headphones are plugged in - -Example: - -sound { - compatible = "nvidia,tegra-audio-rt5640-dalmore", - "nvidia,tegra-audio-rt5640"; - nvidia,model = "NVIDIA Tegra Dalmore"; - - nvidia,audio-routing = - "Headphones", "HPOR", - "Headphones", "HPOL", - "Speakers", "SPORP", - "Speakers", "SPORN", - "Speakers", "SPOLP", - "Speakers", "SPOLN"; - - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&rt5640>; - - nvidia,hp-det-gpios = <&gpio 143 0>; /* GPIO PR7 */ - - clocks = <&tegra_car 216>, <&tegra_car 217>, <&tegra_car 120>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.yaml new file mode 100644 index 000000000000..e768fb0e9a59 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-rt5640.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with RT5639 or RT5640 CODEC + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-rt56(39|40)(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-rt5640 + + 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 + - "Headphones" + - "Speakers" + - "Mic Jack" + + # CODEC Pins + - DMIC1 + - DMIC2 + - MICBIAS1 + - IN1P + - IN1R + - IN2P + - IN2R + - HPOL + - HPOR + - LOUTL + - LOUTR + - MONOP + - MONON + - SPOLP + - SPOLN + - SPORP + - SPORN + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-rt5640-dalmore", + "nvidia,tegra-audio-rt5640"; + nvidia,model = "NVIDIA Tegra Dalmore"; + + nvidia,audio-routing = + "Headphones", "HPOR", + "Headphones", "HPOL", + "Speakers", "SPORP", + "Speakers", "SPORN", + "Speakers", "SPOLP", + "Speakers", "SPOLN"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&rt5640>; + + nvidia,hp-det-gpios = <&gpio 143 0>; + + clocks = <&clk 216>, <&clk 217>, <&clk 120>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; + diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt deleted file mode 100644 index a4589cda214e..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt +++ /dev/null @@ -1,67 +0,0 @@ -NVIDIA Tegra audio complex, with RT5677 CODEC - -Required properties: -- compatible : "nvidia,tegra-audio-rt5677" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,audio-routing : 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 RT5677's pins (as documented in its binding), and the jacks - on the board: - - * Headphone - * Speaker - * Headset Mic - * Internal Mic 1 - * Internal Mic 2 - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's - connected to the CODEC. -- nvidia,audio-codec : The phandle of the RT5677 audio codec. This binding - assumes that AIF1 on the CODEC is connected to Tegra. - -Optional properties: -- nvidia,hp-det-gpios : The GPIO that detects headphones are plugged in -- nvidia,hp-en-gpios : The GPIO that enables headphone amplifier -- nvidia,mic-present-gpios: The GPIO that mic jack is plugged in -- nvidia,dmic-clk-en-gpios : The GPIO that gates DMIC clock signal - -Example: - -sound { - compatible = "nvidia,tegra-audio-rt5677-ryu", - "nvidia,tegra-audio-rt5677"; - nvidia,model = "NVIDIA Tegra Ryu"; - - nvidia,audio-routing = - "Headphone", "LOUT2", - "Headphone", "LOUT1", - "Headset Mic", "MICBIAS1", - "IN1P", "Headset Mic", - "IN1N", "Headset Mic", - "DMIC L1", "Internal Mic 1", - "DMIC R1", "Internal Mic 1", - "DMIC L2", "Internal Mic 2", - "DMIC R2", "Internal Mic 2", - "Speaker", "PDM1L", - "Speaker", "PDM1R"; - - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&rt5677>; - - nvidia,hp-det-gpios = <&gpio TEGRA_GPIO(R, 7) GPIO_ACTIVE_HIGH>; - nvidia,mic-present-gpios = <&gpio TEGRA_GPIO(O, 5) GPIO_ACTIVE_LOW>; - nvidia,hp-en-gpios = <&rt5677 1 GPIO_ACTIVE_HIGH>; - nvidia,dmic-clk-en-gpios = <&rt5677 2 GPIO_ACTIVE_HIGH>; - - clocks = <&tegra_car TEGRA124_CLK_PLL_A>, - <&tegra_car TEGRA124_CLK_PLL_A_OUT0>, - <&tegra_car TEGRA124_CLK_EXTERN1>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml new file mode 100644 index 000000000000..a49997d6028b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-rt5677.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with RT5677 CODEC + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-rt5677(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-rt5677 + + 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 + - "Headphone" + - "Speaker" + - "Headset Mic" + - "Internal Mic 1" + - "Internal Mic 2" + + # CODEC Pins + - IN1P + - IN1N + - IN2P + - IN2N + - MICBIAS1 + - DMIC1 + - DMIC2 + - DMIC3 + - DMIC4 + - "DMIC L1" + - "DMIC L2" + - "DMIC L3" + - "DMIC L4" + - "DMIC R1" + - "DMIC R2" + - "DMIC R3" + - "DMIC R4" + - LOUT1 + - LOUT2 + - LOUT3 + - PDM1L + - PDM1R + - PDM2L + - PDM2R + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-rt5677-ryu", + "nvidia,tegra-audio-rt5677"; + nvidia,model = "NVIDIA Tegra Ryu"; + + nvidia,audio-routing = + "Headphone", "LOUT2", + "Headphone", "LOUT1", + "Headset Mic", "MICBIAS1", + "IN1P", "Headset Mic", + "IN1N", "Headset Mic", + "DMIC L1", "Internal Mic 1", + "DMIC R1", "Internal Mic 1", + "DMIC L2", "Internal Mic 2", + "DMIC R2", "Internal Mic 2", + "Speaker", "PDM1L", + "Speaker", "PDM1R"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&rt5677>; + + nvidia,hp-det-gpios = <&gpio 143 0>; + + clocks = <&clk 216>, + <&clk 217>, + <&clk 121>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt deleted file mode 100644 index 5da7da4ea07a..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt +++ /dev/null @@ -1,42 +0,0 @@ -NVIDIA Tegra audio complex, with SGTL5000 CODEC - -Required properties: -- compatible : "nvidia,tegra-audio-sgtl5000" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,audio-routing : 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 SGTL5000's pins (as documented in its binding), and the jacks - on the board: - - * Headphone Jack - * Line In Jack - * Mic Jack - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's - connected to the CODEC. -- nvidia,audio-codec : The phandle of the SGTL5000 audio codec. - -Example: - -sound { - compatible = "toradex,tegra-audio-sgtl5000-apalis_t30", - "nvidia,tegra-audio-sgtl5000"; - nvidia,model = "Toradex Apalis T30"; - nvidia,audio-routing = - "Headphone Jack", "HP_OUT", - "LINE_IN", "Line In Jack", - "MIC_IN", "Mic Jack"; - nvidia,i2s-controller = <&tegra_i2s2>; - nvidia,audio-codec = <&sgtl5000>; - clocks = <&tegra_car TEGRA30_CLK_PLL_A>, - <&tegra_car TEGRA30_CLK_PLL_A_OUT0>, - <&tegra_car TEGRA30_CLK_EXTERN1>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.yaml new file mode 100644 index 000000000000..943e7c01741c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-sgtl5000.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with SGTL5000 CODEC + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-sgtl5000([-_][a-z0-9]+)+$' + - const: nvidia,tegra-audio-sgtl5000 + + 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 + - "Headphone Jack" + - "Line In Jack" + - "Mic Jack" + + # CODEC Pins + - HP_OUT + - LINE_OUT + - LINE_IN + - MIC_IN + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/tegra30-car.h> + + sound { + compatible = "toradex,tegra-audio-sgtl5000-apalis_t30", + "nvidia,tegra-audio-sgtl5000"; + nvidia,model = "Toradex Apalis T30 SGTL5000"; + nvidia,audio-routing = + "Headphone Jack", "HP_OUT", + "LINE_IN", "Line In Jack", + "MIC_IN", "Mic Jack"; + nvidia,i2s-controller = <&tegra_i2s2>; + nvidia,audio-codec = <&codec>; + clocks = <&tegra_car TEGRA30_CLK_PLL_A>, + <&tegra_car TEGRA30_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA30_CLK_EXTERN1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt deleted file mode 100644 index ef1fe7358279..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt +++ /dev/null @@ -1,21 +0,0 @@ -NVIDIA Tegra audio complex for TrimSlice - -Required properties: -- compatible : "nvidia,tegra-audio-trimslice" -- clocks : Must contain an entry for each entry in clock-names. -- clock-names : Must include the following entries: - "pll_a" (The Tegra clock of that name), - "pll_a_out0" (The Tegra clock of that name), - "mclk" (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller -- nvidia,audio-codec : The phandle of the WM8903 audio codec - -Example: - -sound { - compatible = "nvidia,tegra-audio-trimslice"; - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&codec>; - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.yaml new file mode 100644 index 000000000000..8c87cd166238 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.yaml @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-trimslice.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with TrimSlice CODEC + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + const: nvidia,tegra-audio-trimslice + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-trimslice"; + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&codec>; + clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt deleted file mode 100644 index 96f6a57dd6b4..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt +++ /dev/null @@ -1,40 +0,0 @@ -NVIDIA Tegra audio complex - -Required properties: -- compatible : "nvidia,tegra-audio-wm8753" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,audio-routing : 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 WM8753's pins as documented in the binding for the WM8753, - and the jacks on the board: - - * Headphone Jack - * Mic Jack - -- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller -- nvidia,audio-codec : The phandle of the WM8753 audio codec -Example: - -sound { - compatible = "nvidia,tegra-audio-wm8753-whistler", - "nvidia,tegra-audio-wm8753" - nvidia,model = "tegra-wm8753-harmony"; - - nvidia,audio-routing = - "Headphone Jack", "LOUT1", - "Headphone Jack", "ROUT1"; - - nvidia,i2s-controller = <&i2s1>; - nvidia,audio-codec = <&wm8753>; - - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; - diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.yaml new file mode 100644 index 000000000000..a5b431d7d0c2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-wm8753.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with WM8753 CODEC + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-wm8753(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-wm8753 + + 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 + - "Headphone Jack" + - "Mic Jack" + + # CODEC Pins + - LOUT1 + - LOUT2 + - ROUT1 + - ROUT2 + - MONO1 + - MONO2 + - OUT3 + - OUT4 + - LINE1 + - LINE2 + - RXP + - RXN + - ACIN + - ACOP + - MIC1N + - MIC1 + - MIC2N + - MIC2 + - "Mic Bias" + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-wm8753-whistler", + "nvidia,tegra-audio-wm8753"; + nvidia,model = "tegra-wm8753-harmony"; + + nvidia,audio-routing = + "Headphone Jack", "LOUT1", + "Headphone Jack", "ROUT1"; + + nvidia,i2s-controller = <&i2s1>; + nvidia,audio-codec = <&wm8753>; + + clocks = <&clk 112>, <&clk 113>, <&clk 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt deleted file mode 100644 index bbd581a8c5bc..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt +++ /dev/null @@ -1,62 +0,0 @@ -NVIDIA Tegra audio complex - -Required properties: -- compatible : "nvidia,tegra-audio-wm8903" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,audio-routing : 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 WM8903's pins (documented in the WM8903 binding document), - and the jacks on the board: - - * Headphone Jack - * Int Spk - * Mic Jack - * Int Mic - -- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller -- nvidia,audio-codec : The phandle of the WM8903 audio codec - -Optional properties: -- nvidia,spkr-en-gpios : The GPIO that enables the speakers -- nvidia,hp-mute-gpios : The GPIO that mutes the headphones -- nvidia,hp-det-gpios : The GPIO that detect headphones are plugged in -- nvidia,int-mic-en-gpios : The GPIO that enables the internal microphone -- nvidia,ext-mic-en-gpios : The GPIO that enables the external microphone -- nvidia,headset : The Mic Jack represents state of the headset microphone pin - -Example: - -sound { - compatible = "nvidia,tegra-audio-wm8903-harmony", - "nvidia,tegra-audio-wm8903" - nvidia,model = "tegra-wm8903-harmony"; - - nvidia,audio-routing = - "Headphone Jack", "HPOUTR", - "Headphone Jack", "HPOUTL", - "Int Spk", "ROP", - "Int Spk", "RON", - "Int Spk", "LOP", - "Int Spk", "LON", - "Mic Jack", "MICBIAS", - "IN1L", "Mic Jack"; - - nvidia,i2s-controller = <&i2s1>; - nvidia,audio-codec = <&wm8903>; - - nvidia,spkr-en-gpios = <&codec 2 0>; - nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */ - nvidia,int-mic-en-gpios = <&gpio 184 0>; /*gpio PX0 */ - nvidia,ext-mic-en-gpios = <&gpio 185 0>; /* gpio PX1 */ - - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; - diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.yaml new file mode 100644 index 000000000000..1b836acab980 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-wm8903.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with WM8903 CODEC + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + oneOf: + - items: + - pattern: '^[a-z0-9]+,tegra-audio-wm8903(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-wm8903 + - items: + - pattern: ad,tegra-audio-plutux + - const: nvidia,tegra-audio-wm8903 + + 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 + - "Headphone Jack" + - "Int Spk" + - "Mic Jack" + - "Int Mic" + + # CODEC Pins + - IN1L + - IN1R + - IN2L + - IN2R + - IN3L + - IN3R + - DMICDAT + - HPOUTL + - HPOUTR + - LINEOUTL + - LINEOUTR + - LOP + - LON + - ROP + - RON + - MICBIAS + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-wm8903-harmony", + "nvidia,tegra-audio-wm8903"; + nvidia,model = "tegra-wm8903-harmony"; + + nvidia,audio-routing = + "Headphone Jack", "HPOUTR", + "Headphone Jack", "HPOUTL", + "Int Spk", "ROP", + "Int Spk", "RON", + "Int Spk", "LOP", + "Int Spk", "LON", + "Mic Jack", "MICBIAS", + "IN1L", "Mic Jack"; + + nvidia,i2s-controller = <&i2s1>; + nvidia,audio-codec = <&wm8903>; + + nvidia,spkr-en-gpios = <&codec 2 0>; + nvidia,hp-det-gpios = <&gpio 178 0>; + nvidia,int-mic-en-gpios = <&gpio 184 0>; + nvidia,ext-mic-en-gpios = <&gpio 185 0>; + + clocks = <&clk 112>, <&clk 113>, <&clk 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt deleted file mode 100644 index 436f6cd9d07c..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt +++ /dev/null @@ -1,60 +0,0 @@ -NVIDIA Tegra audio complex - -Required properties: -- compatible : "nvidia,tegra-audio-wm9712" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,audio-routing : 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 WM9712's pins, and the jacks on the board: - - WM9712 pins: - - * MONOOUT - * HPOUTL - * HPOUTR - * LOUT2 - * ROUT2 - * OUT3 - * LINEINL - * LINEINR - * PHONE - * PCBEEP - * MIC1 - * MIC2 - * Mic Bias - - Board connectors: - - * Headphone - * LineIn - * Mic - -- nvidia,ac97-controller : The phandle of the Tegra AC97 controller - - -Example: - -sound { - compatible = "nvidia,tegra-audio-wm9712-colibri_t20", - "nvidia,tegra-audio-wm9712"; - nvidia,model = "Toradex Colibri T20"; - - nvidia,audio-routing = - "Headphone", "HPOUTL", - "Headphone", "HPOUTR", - "LineIn", "LINEINL", - "LineIn", "LINEINR", - "Mic", "MIC1"; - - nvidia,ac97-controller = <&ac97>; - - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.yaml new file mode 100644 index 000000000000..a1448283344b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-wm9712.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with WM9712 CODEC + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Thierry Reding <thierry.reding@gmail.com> + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-wm9712([-_][a-z0-9]+)+$' + - const: nvidia,tegra-audio-wm9712 + + 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 + - "Headphone" + - "LineIn" + - "Mic" + + # CODEC Pins + - MONOOUT + - HPOUTL + - HPOUTR + - LOUT2 + - ROUT2 + - OUT3 + - LINEINL + - LINEINR + - PHONE + - PCBEEP + - MIC1 + - MIC2 + - "Mic Bias" + +required: + - nvidia,ac97-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-wm9712-colibri_t20", + "nvidia,tegra-audio-wm9712"; + nvidia,model = "Toradex Colibri T20"; + + nvidia,audio-routing = + "Headphone", "HPOUTL", + "Headphone", "HPOUTR", + "LineIn", "LINEINL", + "LineIn", "LINEINR", + "Mic", "MIC1"; + + nvidia,ac97-controller = <&ac97>; + + clocks = <&clk 112>, <&clk 113>, <&clk 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt deleted file mode 100644 index dc30c6bfbe95..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt +++ /dev/null @@ -1,30 +0,0 @@ -NVIDIA Tegra 20 I2S controller - -Required properties: -- compatible : "nvidia,tegra20-i2s" -- reg : Should contain I2S registers location and length -- interrupts : Should contain I2S interrupt -- resets : Must contain an entry for each entry in reset-names. - See ../reset/reset.txt for details. -- reset-names : Must include the following entries: - - i2s -- dmas : Must contain an entry for each entry in clock-names. - See ../dma/dma.txt for details. -- dma-names : Must include the following entries: - - rx - - tx -- clocks : Must contain one entry, for the module clock. - See ../clocks/clock-bindings.txt for details. - -Example: - -i2s@70002800 { - compatible = "nvidia,tegra20-i2s"; - reg = <0x70002800 0x200>; - interrupts = < 45 >; - clocks = <&tegra_car 11>; - resets = <&tegra_car 11>; - reset-names = "i2s"; - dmas = <&apbdma 21>, <&apbdma 21>; - dma-names = "rx", "tx"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml new file mode 100644 index 000000000000..68ae124eaf80 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra20-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra20 I2S Controller + +description: | + The I2S Controller streams synchronous serial audio data between system + memory and an external audio device. The controller supports the I2S Left + Justified Mode, Right Justified Mode, and DSP mode formats. + +maintainers: + - Thierry Reding <treding@nvidia.com> + - Jon Hunter <jonathanh@nvidia.com> + +properties: + compatible: + const: nvidia,tegra20-i2s + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + reset-names: + const: i2s + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + + dmas: + minItems: 2 + + dma-names: + items: + - const: rx + - const: tx + + nvidia,fixed-parent-rate: + description: | + Specifies whether board prefers parent clock to stay at a fixed rate. + This allows multiple Tegra20 audio components work simultaneously by + limiting number of supportable audio rates. + type: boolean + +required: + - compatible + - reg + - resets + - reset-names + - interrupts + - clocks + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + i2s@70002800 { + compatible = "nvidia,tegra20-i2s"; + reg = <0x70002800 0x200>; + interrupts = <45>; + clocks = <&tegra_car 11>; + resets = <&tegra_car 11>; + reset-names = "i2s"; + dmas = <&apbdma 21>, <&apbdma 21>; + dma-names = "rx", "tx"; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml new file mode 100644 index 000000000000..60a368a132b8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra20-spdif.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra20 S/PDIF Controller + +description: | + The S/PDIF controller supports both input and output in serial audio + digital interface format. The input controller can digitally recover + a clock from the received stream. The S/PDIF controller is also used + to generate the embedded audio for HDMI output channel. + +maintainers: + - Thierry Reding <treding@nvidia.com> + - Jon Hunter <jonathanh@nvidia.com> + +properties: + compatible: + const: nvidia,tegra20-spdif + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 2 + + clock-names: + items: + - const: out + - const: in + + dmas: + minItems: 2 + + dma-names: + items: + - const: rx + - const: tx + + "#sound-dai-cells": + const: 0 + + nvidia,fixed-parent-rate: + description: | + Specifies whether board prefers parent clock to stay at a fixed rate. + This allows multiple Tegra20 audio components work simultaneously by + limiting number of supportable audio rates. + type: boolean + +required: + - compatible + - reg + - resets + - interrupts + - clocks + - clock-names + - dmas + - dma-names + - "#sound-dai-cells" + +additionalProperties: false + +examples: + - | + spdif@70002400 { + compatible = "nvidia,tegra20-spdif"; + reg = <0x70002400 0x200>; + interrupts = <77>; + clocks = <&clk 99>, <&clk 98>; + clock-names = "out", "in"; + resets = <&rst 10>; + dmas = <&apbdma 3>, <&apbdma 3>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml index b55775e21de6..2c913aa44fee 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml @@ -50,9 +50,11 @@ properties: - const: hda2codec_2x resets: + minItems: 2 maxItems: 3 reset-names: + minItems: 2 items: - const: hda - const: hda2hdmi diff --git a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml index 7667471be1e4..b9b1dba40856 100644 --- a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml +++ b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml @@ -24,11 +24,23 @@ properties: '#sound-dai-cells': const: 0 + rcv-gpios: + description: optional GPIO to be asserted when receiver mode is enabled. + sound-name-prefix: true vddd-supply: description: regulator phandle for the VDDD power supply. +if: + not: + properties: + compatible: + const: nxp,tfa9897 +then: + properties: + rcv-gpios: false + required: - compatible - reg @@ -55,3 +67,32 @@ examples: #sound-dai-cells = <0>; }; }; + + - | + #include <dt-bindings/gpio/gpio.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + speaker_codec_top: audio-codec@34 { + compatible = "nxp,tfa9897"; + reg = <0x34>; + vddd-supply = <&pm8916_l6>; + rcv-gpios = <&msmgpio 50 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&speaker_top_default>; + sound-name-prefix = "Speaker Top"; + #sound-dai-cells = <0>; + }; + + speaker_codec_bottom: audio-codec@36 { + compatible = "nxp,tfa9897"; + reg = <0x36>; + vddd-supply = <&pm8916_l6>; + rcv-gpios = <&msmgpio 111 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&speaker_bottom_default>; + sound-name-prefix = "Speaker Bottom"; + #sound-dai-cells = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt b/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt deleted file mode 100644 index 23998262a0a7..000000000000 --- a/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt +++ /dev/null @@ -1,96 +0,0 @@ -* Qualcomm Technologies APQ8016 SBC ASoC machine driver - -This node models the Qualcomm Technologies APQ8016 SBC ASoC machine driver - -Required properties: - -- compatible : "qcom,apq8016-sbc-sndcard" - -- pinctrl-N : One property must exist for each entry in - pinctrl-names. See ../pinctrl/pinctrl-bindings.txt - for details of the property values. -- pinctrl-names : Must contain a "default" entry. -- reg : Must contain an address for each entry in reg-names. -- reg-names : A list which must include the following entries: - * "mic-iomux" - * "spkr-iomux" -- qcom,model : Name of the sound card. - -- qcom,audio-routing : 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 could be power supplies, MicBias - of msm8x16_wcd codec and the jacks on the board: - - Power supplies: - * MIC BIAS External1 - * MIC BIAS External2 - * MIC BIAS Internal1 - * MIC BIAS Internal2 - - Board connectors: - * Headset Mic - * Secondary Mic - * DMIC - * Ext Spk - -Optional properties: - -- aux-devs : A list of phandles for auxiliary devices (e.g. analog - amplifiers) that do not appear directly within the DAI - links. Should be connected to another audio component - using "qcom,audio-routing". - -Dai-link subnode properties and subnodes: - -Required dai-link subnodes: - -- cpu : CPU sub-node -- codec : CODEC sub-node - -Required CPU/CODEC subnodes properties: - --link-name : Name of the dai link. --sound-dai : phandle/s and port of CPU/CODEC - -Example: - -sound: sound { - compatible = "qcom,apq8016-sbc-sndcard"; - reg = <0x07702000 0x4>, <0x07702004 0x4>; - reg-names = "mic-iomux", "spkr-iomux"; - qcom,model = "DB410c"; - - qcom,audio-routing = - "MIC BIAS External1", "Handset Mic", - "MIC BIAS Internal2", "Headset Mic", - "MIC BIAS External1", "Secondary Mic", - "AMIC1", "MIC BIAS External1", - "AMIC2", "MIC BIAS Internal2", - "AMIC3", "MIC BIAS External1", - "DMIC1", "MIC BIAS Internal1", - "MIC BIAS Internal1", "Digital Mic1", - "DMIC2", "MIC BIAS Internal1", - "MIC BIAS Internal1", "Digital Mic2"; - - /* I2S - Internal codec */ - internal-dai-link@0 { - cpu { /* PRIMARY */ - sound-dai = <&lpass MI2S_PRIMARY>; - }; - codec { - sound-dai = <&lpass_codec 0>, <&wcd_codec 0>; - }; - }; - - /* External Primary or External Secondary -ADV7533 HDMI */ - external-dai-link@0 { - link-name = "ADV7533"; - cpu { /* QUAT */ - sound-dai = <&lpass MI2S_QUATERNARY>; - }; - codec { - sound-dai = <&adv_bridge 0>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 7d57eb91657a..4bfda04b4608 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -4,18 +4,20 @@ $id: http://devicetree.org/schemas/sound/qcom,sm8250.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm Technologies Inc. SM8250 ASoC sound card driver +title: Qualcomm Technologies Inc. ASoC sound card drivers maintainers: - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> description: - This bindings describes SC8250 SoC based sound cards + This bindings describes Qualcomm SoC based sound cards which uses LPASS internal codec for audio. properties: compatible: enum: + - qcom,apq8016-sbc-sndcard + - qcom,msm8916-qdsp6-sndcard - qcom,sm8250-sndcard - qcom,qrb5165-rb5-sndcard @@ -27,10 +29,28 @@ properties: being the connection's source. Valid names could be power supplies, MicBias of codec and the jacks on the board. + aux-devs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + List of phandles pointing to auxiliary devices, such + as amplifiers, to be added to the sound card. + model: $ref: /schemas/types.yaml#/definitions/string description: User visible long sound card name + pin-switches: + description: List of widget names for which pin switches should be created. + $ref: /schemas/types.yaml#/definitions/string-array + + widgets: + description: User specified audio sound widgets. + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + + # Only valid for some compatibles (see allOf if below) + reg: true + reg-names: true + patternProperties: ".*-dai-link$": description: @@ -73,6 +93,34 @@ required: - compatible - model +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,apq8016-sbc-sndcard + - qcom,msm8916-qdsp6-sndcard + then: + properties: + reg: + items: + - description: Microphone I/O mux register address + - description: Speaker I/O mux register address + reg-names: + items: + - const: mic-iomux + - const: spkr-iomux + required: + - compatible + - model + - reg + - reg-names + else: + properties: + reg: false + reg-names: false + additionalProperties: false examples: @@ -86,10 +134,7 @@ examples: audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT", "SpkrRight IN", "WSA_SPK2 OUT", "VA DMIC0", "vdd-micb", - "VA DMIC1", "vdd-micb", - "MM_DL1", "MultiMedia1 Playback", - "MM_DL2", "MultiMedia2 Playback", - "MultiMedia3 Capture", "MM_UL3"; + "VA DMIC1", "vdd-micb"; mm1-dai-link { link-name = "MultiMedia0"; @@ -157,3 +202,98 @@ examples: }; }; }; + + - | + #include <dt-bindings/sound/qcom,lpass.h> + sound@7702000 { + compatible = "qcom,apq8016-sbc-sndcard"; + reg = <0x07702000 0x4>, <0x07702004 0x4>; + reg-names = "mic-iomux", "spkr-iomux"; + + model = "DB410c"; + audio-routing = + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS External1"; + + pinctrl-0 = <&cdc_pdm_lines_act &ext_sec_tlmm_lines_act &ext_mclk_tlmm_lines_act>; + pinctrl-1 = <&cdc_pdm_lines_sus &ext_sec_tlmm_lines_sus &ext_mclk_tlmm_lines_sus>; + pinctrl-names = "default", "sleep"; + + quaternary-dai-link { + link-name = "ADV7533"; + cpu { + sound-dai = <&lpass MI2S_QUATERNARY>; + }; + codec { + sound-dai = <&adv_bridge 0>; + }; + }; + + primary-dai-link { + link-name = "WCD"; + cpu { + sound-dai = <&lpass MI2S_PRIMARY>; + }; + codec { + sound-dai = <&lpass_codec 0>, <&wcd_codec 0>; + }; + }; + + tertiary-dai-link { + link-name = "WCD-Capture"; + cpu { + sound-dai = <&lpass MI2S_TERTIARY>; + }; + codec { + sound-dai = <&lpass_codec 1>, <&wcd_codec 1>; + }; + }; + }; + + - | + #include <dt-bindings/sound/qcom,q6afe.h> + #include <dt-bindings/sound/qcom,q6asm.h> + sound@7702000 { + compatible = "qcom,msm8916-qdsp6-sndcard"; + reg = <0x07702000 0x4>, <0x07702004 0x4>; + reg-names = "mic-iomux", "spkr-iomux"; + + model = "msm8916"; + widgets = + "Speaker", "Speaker", + "Headphone", "Headphones"; + pin-switches = "Speaker"; + audio-routing = + "Speaker", "Speaker Amp OUT", + "Speaker Amp IN", "HPH_R", + "Headphones", "HPH_L", + "Headphones", "HPH_R", + "AMIC1", "MIC BIAS Internal1", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS Internal3"; + aux-devs = <&speaker_amp>; + + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&cdc_pdm_lines_act>; + pinctrl-1 = <&cdc_pdm_lines_sus>; + + mm1-dai-link { + link-name = "MultiMedia1"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + primary-dai-link { + link-name = "Primary MI2S"; + cpu { + sound-dai = <&q6afedai PRIMARY_MI2S_RX>; + }; + platform { + sound-dai = <&q6routing>; + }; + codec { + sound-dai = <&lpass_codec 0>, <&wcd_codec 0>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml index 2b8b7b51fe55..d65c0ed5060c 100644 --- a/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml @@ -61,6 +61,10 @@ properties: description: | Set the delay time (ms) for the requirement of the particular DMIC. + realtek,amic-delay-ms: + description: | + Set the delay time (ms) for the requirement of the particular platform or AMIC. + realtek,dmic-clk-driving-high: type: boolean description: | diff --git a/Documentation/devicetree/bindings/sound/simple-audio-amplifier.yaml b/Documentation/devicetree/bindings/sound/simple-audio-amplifier.yaml index 26379377a7ac..8327846356d3 100644 --- a/Documentation/devicetree/bindings/sound/simple-audio-amplifier.yaml +++ b/Documentation/devicetree/bindings/sound/simple-audio-amplifier.yaml @@ -9,6 +9,9 @@ title: Simple Audio Amplifier Device Tree Bindings maintainers: - Jerome Brunet <jbrunet@baylibre.com> +allOf: + - $ref: name-prefix.yaml# + properties: compatible: enum: @@ -22,10 +25,7 @@ properties: description: > power supply for the device - sound-name-prefix: - $ref: /schemas/types.yaml#/definitions/string - description: > - See ./name-prefix.txt + sound-name-prefix: true required: - compatible diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml new file mode 100644 index 000000000000..83936f594d1a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tlv320adc3xxx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TLV320ADC3001/TLV320ADC3101 Stereo ADC + +maintainers: + - Ricard Wanderlof <ricardw@axis.com> + +description: | + Texas Instruments TLV320ADC3001 and TLV320ADC3101 Stereo ADC + https://www.ti.com/product/TLV320ADC3001 + https://www.ti.com/product/TLV320ADC3101 + +properties: + compatible: + enum: + - ti,tlv320adc3001 + - ti,tlv320adc3101 + + reg: + maxItems: 1 + description: I2C address + + '#sound-dai-cells': + const: 0 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + reset-gpios: + maxItems: 1 + description: GPIO pin used for codec reset (RESET pin) + + clocks: + maxItems: 1 + description: Master clock (MCLK) + + ti,dmdin-gpio1: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # ADC3XXX_GPIO_DISABLED - I/O buffers powered down and not used + - 1 # ADC3XXX_GPIO_INPUT - Various non-GPIO input functions + - 2 # ADC3XXX_GPIO_GPI - General purpose input + - 3 # ADC3XXX_GPIO_GPO - General purpose output + - 4 # ADC3XXX_GPIO_CLKOUT - Clock source set in CLKOUT_MUX reg + - 5 # ADC3XXX_GPIO_INT1 - INT1 output + - 6 # ADC3XXX_GPIO_SECONDARY_BCLK - Codec interface secondary BCLK + - 7 # ADC3XXX_GPIO_SECONDARY_WCLK - Codec interface secondary WCLK + default: 0 + description: | + Configuration for DMDIN/GPIO1 pin. + + When ADC3XXX_GPIO_GPO is configured, this causes corresponding the + ALSA control "GPIOx Output" to appear, as a switch control. + + ti,dmclk-gpio2: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # ADC3XXX_GPIO_DISABLED - I/O buffers powered down and not used + - 1 # ADC3XXX_GPIO_INPUT - Various non-GPIO input functions + - 2 # ADC3XXX_GPIO_GPI - General purpose input + - 3 # ADC3XXX_GPIO_GPO - General purpose output + - 4 # ADC3XXX_GPIO_CLKOUT - Clock source set in CLKOUT_MUX reg + - 5 # ADC3XXX_GPIO_INT1 - INT1 output + - 6 # ADC3XXX_GPIO_SECONDARY_BCLK - Codec interface secondary BCLK + - 7 # ADC3XXX_GPIO_SECONDARY_WCLK - Codec interface secondary WCLK + default: 0 + description: | + Configuration for DMCLK/GPIO2 pin. + + When ADC3XXX_GPIO_GPO is configured, this causes corresponding the + ALSA control "GPIOx Output" to appear, as a switch control. + + Note that there is currently no support for reading the GPIO pins as + inputs. + + ti,micbias1-vg: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # ADC3XXX_MICBIAS_OFF - Mic bias is powered down + - 1 # ADC3XXX_MICBIAS_2_0V - Mic bias is set to 2.0V + - 2 # ADC3XXX_MICBIAS_2_5V - Mic bias is set to 2.5V + - 3 # ADC3XXX_MICBIAS_AVDD - Mic bias is same as AVDD supply + default: 0 + description: | + Mic bias voltage output on MICBIAS1 pin + + ti,micbias2-vg: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # ADC3XXX_MICBIAS_OFF - Mic bias is powered down + - 1 # ADC3XXX_MICBIAS_2_0V - Mic bias is set to 2.0V + - 2 # ADC3XXX_MICBIAS_2_5V - Mic bias is set to 2.5V + - 3 # ADC3XXX_MICBIAS_AVDD - Mic bias is same as AVDD supply + default: 0 + description: | + Mic bias voltage output on MICBIAS2 pin + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/sound/tlv320adc3xxx.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + tlv320adc3101: audio-codec@18 { + compatible = "ti,tlv320adc3101"; + reg = <0x18>; + reset-gpios = <&gpio_pc 3 GPIO_ACTIVE_LOW>; + clocks = <&audio_mclk>; + gpio-controller; + #gpio-cells = <2>; + ti,dmdin-gpio1 = <ADC3XXX_GPIO_GPO>; + ti,micbias1-vg = <ADC3XXX_MICBIAS_AVDD>; + }; + }; + + audio_mclk: clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + }; +... diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml new file mode 100644 index 000000000000..7105ed5fd6c7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml @@ -0,0 +1,116 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/sound/wlf,wm8903.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: WM8903 audio codec + +description: | + This device supports I2C only. + Pins on the device (for linking into audio routes): + * IN1L + * IN1R + * IN2L + * IN2R + * IN3L + * IN3R + * DMICDAT + * HPOUTL + * HPOUTR + * LINEOUTL + * LINEOUTR + * LOP + * LON + * ROP + * RON + * MICBIAS + +maintainers: + - patches@opensource.cirrus.com + +properties: + compatible: + const: wlf,wm8903 + + reg: + maxItems: 1 + + gpio-controller: true + '#gpio-cells': + const: 2 + + interrupts: + maxItems: 1 + + micdet-cfg: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + description: Default register value for R6 (Mic Bias). + + micdet-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 100 + description: The debounce delay for microphone detection in mS. + + gpio-cfg: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + minItems: 5 + maxItems: 5 + A list of GPIO configuration register values. + If absent, no configuration of these registers is performed. + If any entry has the value 0xffffffff, that GPIO's + configuration will not be modified. + + AVDD-supply: + description: Analog power supply regulator on the AVDD pin. + + CPVDD-supply: + description: Charge pump supply regulator on the CPVDD pin. + + DBVDD-supply: + description: Digital buffer supply regulator for the DBVDD pin. + + DCVDD-supply: + description: Digital core supply regulator for the DCVDD pin. + + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + wm8903: codec@1a { + compatible = "wlf,wm8903"; + reg = <0x1a>; + interrupts = <347>; + + AVDD-supply = <&fooreg_a>; + CPVDD-supply = <&fooreg_b>; + DBVDD-supply = <&fooreg_c>; + DCVDD-supply = <&fooreg_d>; + + gpio-controller; + #gpio-cells = <2>; + + micdet-cfg = <0>; + micdet-delay = <100>; + gpio-cfg = < + 0x0600 /* DMIC_LR, output */ + 0x0680 /* DMIC_DAT, input */ + 0x0000 /* GPIO, output, low */ + 0x0200 /* Interrupt, output */ + 0x01a0 /* BCLK, input, active high */ + >; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/wm8903.txt b/Documentation/devicetree/bindings/sound/wm8903.txt deleted file mode 100644 index 6371c2434afe..000000000000 --- a/Documentation/devicetree/bindings/sound/wm8903.txt +++ /dev/null @@ -1,82 +0,0 @@ -WM8903 audio CODEC - -This device supports I2C only. - -Required properties: - - - compatible : "wlf,wm8903" - - - reg : the I2C address of the device. - - - gpio-controller : Indicates this device is a GPIO controller. - - - #gpio-cells : Should be two. The first cell is the pin number and the - second cell is used to specify optional parameters (currently unused). - -Optional properties: - - - interrupts : The interrupt line the codec is connected to. - - - micdet-cfg : Default register value for R6 (Mic Bias). If absent, the - default is 0. - - - micdet-delay : The debounce delay for microphone detection in mS. If - absent, the default is 100. - - - gpio-cfg : A list of GPIO configuration register values. The list must - be 5 entries long. If absent, no configuration of these registers is - performed. If any entry has the value 0xffffffff, that GPIO's - configuration will not be modified. - - - AVDD-supply : Analog power supply regulator on the AVDD pin. - - - CPVDD-supply : Charge pump supply regulator on the CPVDD pin. - - - DBVDD-supply : Digital buffer supply regulator for the DBVDD pin. - - - DCVDD-supply : Digital core supply regulator for the DCVDD pin. - -Pins on the device (for linking into audio routes): - - * IN1L - * IN1R - * IN2L - * IN2R - * IN3L - * IN3R - * DMICDAT - * HPOUTL - * HPOUTR - * LINEOUTL - * LINEOUTR - * LOP - * LON - * ROP - * RON - * MICBIAS - -Example: - -wm8903: codec@1a { - compatible = "wlf,wm8903"; - reg = <0x1a>; - interrupts = < 347 >; - - AVDD-supply = <&fooreg_a>; - CPVDD-supply = <&fooreg_b>; - DBVDD-supply = <&fooreg_c>; - DCVDC-supply = <&fooreg_d>; - - gpio-controller; - #gpio-cells = <2>; - - micdet-cfg = <0>; - micdet-delay = <100>; - gpio-cfg = < - 0x0600 /* DMIC_LR, output */ - 0x0680 /* DMIC_DAT, input */ - 0x0000 /* GPIO, output, low */ - 0x0200 /* Interrupt, output */ - 0x01a0 /* BCLK, input, active high */ - >; -}; diff --git a/MAINTAINERS b/MAINTAINERS index efc95223d921..3b705e776cbc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4568,9 +4568,12 @@ F: drivers/media/cec/i2c/ch7322.c CIRRUS LOGIC AUDIO CODEC DRIVERS M: James Schulman <james.schulman@cirrus.com> M: David Rhodes <david.rhodes@cirrus.com> +M: Lucas Tanure <tanureal@opensource.cirrus.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: patches@opensource.cirrus.com S: Maintained +F: Documentation/devicetree/bindings/sound/cirrus,cs* +F: sound/pci/hda/cs* F: sound/soc/codecs/cs* CIRRUS LOGIC DSP FIRMWARE DRIVER @@ -17988,6 +17991,7 @@ F: Documentation/sound/ F: include/sound/ F: include/uapi/sound/ F: sound/ +F: tools/testing/selftests/alsa SOUND - COMPRESSED AUDIO M: Vinod Koul <vkoul@kernel.org> @@ -18007,6 +18011,13 @@ F: include/sound/dmaengine_pcm.h F: sound/core/pcm_dmaengine.c F: sound/soc/soc-generic-dmaengine-pcm.c +SOUND - ALSA SELFTESTS +M: Mark Brown <broonie@kernel.org> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +L: linux-kselftest@vger.kernel.org +S: Supported +F: tools/testing/selftests/alsa + SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC) M: Liam Girdwood <lgirdwood@gmail.com> M: Mark Brown <broonie@kernel.org> diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi index 3c4acfca459d..2d48c3715fc6 100644 --- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi @@ -995,9 +995,8 @@ <&bpmp TEGRA194_CLK_HDA2CODEC_2X>; clock-names = "hda", "hda2hdmi", "hda2codec_2x"; resets = <&bpmp TEGRA194_RESET_HDA>, - <&bpmp TEGRA194_RESET_HDA2HDMICODEC>, - <&bpmp TEGRA194_RESET_HDA2CODEC_2X>; - reset-names = "hda", "hda2hdmi", "hda2codec_2x"; + <&bpmp TEGRA194_RESET_HDA2HDMICODEC>; + reset-names = "hda", "hda2hdmi"; power-domains = <&bpmp TEGRA194_POWER_DOMAIN_DISP>; interconnects = <&mc TEGRA194_MEMORY_CLIENT_HDAR &emc>, <&mc TEGRA194_MEMORY_CLIENT_HDAW &emc>; diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index a23563cd118b..5a53d7fcef01 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -727,12 +727,6 @@ static int mmp_pdma_config_write(struct dma_chan *dchan, chan->dir = direction; chan->dev_addr = addr; - /* FIXME: drivers should be ported over to use the filter - * function. Once that's done, the following two lines can - * be removed. - */ - if (cfg->slave_id) - chan->drcmr = cfg->slave_id; return 0; } diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c index 52d04641e361..6078cc81892e 100644 --- a/drivers/dma/pxa_dma.c +++ b/drivers/dma/pxa_dma.c @@ -909,13 +909,6 @@ static void pxad_get_config(struct pxad_chan *chan, *dcmd |= PXA_DCMD_BURST16; else if (maxburst == 32) *dcmd |= PXA_DCMD_BURST32; - - /* FIXME: drivers should be ported over to use the filter - * function. Once that's done, the following two lines can - * be removed. - */ - if (chan->cfg.slave_id) - chan->drcmr = chan->cfg.slave_id; } static struct dma_async_tx_descriptor * diff --git a/drivers/dma/qcom/qcom_adm.c b/drivers/dma/qcom/qcom_adm.c index ee78bed8d60d..facdacf8aede 100644 --- a/drivers/dma/qcom/qcom_adm.c +++ b/drivers/dma/qcom/qcom_adm.c @@ -8,6 +8,7 @@ #include <linux/device.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> +#include <linux/dma/qcom_adm.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -140,6 +141,8 @@ struct adm_chan { struct adm_async_desc *curr_txd; struct dma_slave_config slave; + u32 crci; + u32 mux; struct list_head node; int error; @@ -379,8 +382,8 @@ static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, return ERR_PTR(-EINVAL); } - crci = achan->slave.slave_id & 0xf; - if (!crci || achan->slave.slave_id > 0x1f) { + crci = achan->crci & 0xf; + if (!crci || achan->crci > 0x1f) { dev_err(adev->dev, "invalid crci value\n"); return ERR_PTR(-EINVAL); } @@ -403,9 +406,7 @@ static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, if (!async_desc) return ERR_PTR(-ENOMEM); - if (crci) - async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ? - ADM_CRCI_CTL_MUX_SEL : 0; + async_desc->mux = achan->mux ? ADM_CRCI_CTL_MUX_SEL : 0; async_desc->crci = crci; async_desc->blk_size = blk_size; async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) + @@ -488,10 +489,13 @@ static int adm_terminate_all(struct dma_chan *chan) static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) { struct adm_chan *achan = to_adm_chan(chan); + struct qcom_adm_peripheral_config *config = cfg->peripheral_config; unsigned long flag; spin_lock_irqsave(&achan->vc.lock, flag); memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config)); + if (cfg->peripheral_size == sizeof(config)) + achan->crci = config->crci; spin_unlock_irqrestore(&achan->vc.lock, flag); return 0; @@ -694,6 +698,45 @@ static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, achan->vc.desc_free = adm_dma_free_desc; } +/** + * adm_dma_xlate + * @dma_spec: pointer to DMA specifier as found in the device tree + * @ofdma: pointer to DMA controller data + * + * This can use either 1-cell or 2-cell formats, the first cell + * identifies the slave device, while the optional second cell + * contains the crci value. + * + * Returns pointer to appropriate dma channel on success or NULL on error. + */ +static struct dma_chan *adm_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct dma_device *dev = ofdma->of_dma_data; + struct dma_chan *chan, *candidate = NULL; + struct adm_chan *achan; + + if (!dev || dma_spec->args_count > 2) + return NULL; + + list_for_each_entry(chan, &dev->channels, device_node) + if (chan->chan_id == dma_spec->args[0]) { + candidate = chan; + break; + } + + if (!candidate) + return NULL; + + achan = to_adm_chan(candidate); + if (dma_spec->args_count == 2) + achan->crci = dma_spec->args[1]; + else + achan->crci = 0; + + return dma_get_slave_channel(candidate); +} + static int adm_dma_probe(struct platform_device *pdev) { struct adm_device *adev; @@ -838,8 +881,7 @@ static int adm_dma_probe(struct platform_device *pdev) goto err_disable_clks; } - ret = of_dma_controller_register(pdev->dev.of_node, - of_dma_xlate_by_chan_id, + ret = of_dma_controller_register(pdev->dev.of_node, adm_dma_xlate, &adev->common); if (ret) goto err_unregister_dma; diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 7f72b3f4cd1a..41c6bc650fa3 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -787,14 +787,6 @@ static int shdma_config(struct dma_chan *chan, return -EINVAL; /* - * overriding the slave_id through dma_slave_config is deprecated, - * but possibly some out-of-tree drivers still do it. - */ - if (WARN_ON_ONCE(config->slave_id && - config->slave_id != schan->real_slave_id)) - schan->real_slave_id = config->slave_id; - - /* * We could lock this, but you shouldn't be configuring the * channel, while using it... */ diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 4357d2395e6b..7f158ef5672d 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -795,9 +795,6 @@ static int sprd_dma_fill_desc(struct dma_chan *chan, return dst_datawidth; } - if (slave_cfg->slave_id) - schan->dev_id = slave_cfg->slave_id; - hw->cfg = SPRD_DMA_DONOT_WAIT_BDONE << SPRD_DMA_WAIT_BDONE_OFFSET; /* diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index b7260749e8ee..eaafcbe4ca94 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -343,12 +343,6 @@ static int tegra_dma_slave_config(struct dma_chan *dc, } memcpy(&tdc->dma_sconfig, sconfig, sizeof(*sconfig)); - if (tdc->slave_id == TEGRA_APBDMA_SLAVE_ID_INVALID && - sconfig->device_fc) { - if (sconfig->slave_id > TEGRA_APBDMA_CSR_REQ_SEL_MASK) - return -EINVAL; - tdc->slave_id = sconfig->slave_id; - } tdc->config_init = true; return 0; diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index ce5c66e6897d..b0f4948b00a5 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -12,6 +12,7 @@ #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/delay.h> +#include <linux/dma/xilinx_dpdma.h> #include <linux/dmaengine.h> #include <linux/dmapool.h> #include <linux/interrupt.h> @@ -1273,6 +1274,7 @@ static int xilinx_dpdma_config(struct dma_chan *dchan, struct dma_slave_config *config) { struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_dpdma_peripheral_config *pconfig; unsigned long flags; /* @@ -1282,15 +1284,18 @@ static int xilinx_dpdma_config(struct dma_chan *dchan, * fixed both on the DPDMA side and on the DP controller side. */ - spin_lock_irqsave(&chan->lock, flags); - /* - * Abuse the slave_id to indicate that the channel is part of a video - * group. + * Use the peripheral_config to indicate that the channel is part + * of a video group. This requires matching use of the custom + * structure in each driver. */ - if (chan->id <= ZYNQMP_DPDMA_VIDEO2) - chan->video_group = config->slave_id != 0; + pconfig = config->peripheral_config; + if (WARN_ON(pconfig && config->peripheral_size != sizeof(*pconfig))) + return -EINVAL; + spin_lock_irqsave(&chan->lock, flags); + if (chan->id <= ZYNQMP_DPDMA_VIDEO2 && pconfig) + chan->video_group = pconfig->video_group; spin_unlock_irqrestore(&chan->lock, flags); return 0; diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 948dd8382686..e48108e694f8 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -12,16 +12,10 @@ #include <linux/ctype.h> #include <linux/debugfs.h> #include <linux/delay.h> -#include <linux/device.h> -#include <linux/firmware.h> -#include <linux/interrupt.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/regmap.h> #include <linux/slab.h> #include <linux/vmalloc.h> -#include <linux/workqueue.h> #include <linux/firmware/cirrus/cs_dsp.h> #include <linux/firmware/cirrus/wmfw.h> @@ -622,7 +616,8 @@ static void cs_dsp_halo_show_fw_status(struct cs_dsp *dsp) offs[0], offs[1], offs[2], offs[3]); } -static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg) +static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg, + unsigned int off) { const struct cs_dsp_alg_region *alg_region = &ctl->alg_region; struct cs_dsp *dsp = ctl->dsp; @@ -635,7 +630,7 @@ static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg return -EINVAL; } - *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset); + *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset + off); return 0; } @@ -659,10 +654,12 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int unsigned int reg; int i, ret; + lockdep_assert_held(&dsp->pwr_lock); + if (!dsp->running) return -EPERM; - ret = cs_dsp_coeff_base_reg(ctl, ®); + ret = cs_dsp_coeff_base_reg(ctl, ®, 0); if (ret) return ret; @@ -716,14 +713,14 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_acked_control); static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, - const void *buf, size_t len) + unsigned int off, const void *buf, size_t len) { struct cs_dsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - ret = cs_dsp_coeff_base_reg(ctl, ®); + ret = cs_dsp_coeff_base_reg(ctl, ®, off); if (ret) return ret; @@ -749,38 +746,49 @@ static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, /** * cs_dsp_coeff_write_ctrl() - Writes the given buffer to the given coefficient control * @ctl: pointer to coefficient control + * @off: word offset at which data should be written * @buf: the buffer to write to the given control - * @len: the length of the buffer + * @len: the length of the buffer in bytes * * Must be called with pwr_lock held. * * Return: Zero for success, a negative number on error. */ -int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, const void *buf, size_t len) +int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, + unsigned int off, const void *buf, size_t len) { int ret = 0; + if (!ctl) + return -ENOENT; + + lockdep_assert_held(&ctl->dsp->pwr_lock); + + if (len + off * sizeof(u32) > ctl->len) + return -EINVAL; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) ret = -EPERM; else if (buf != ctl->cache) - memcpy(ctl->cache, buf, len); + memcpy(ctl->cache + off * sizeof(u32), buf, len); ctl->set = 1; if (ctl->enabled && ctl->dsp->running) - ret = cs_dsp_coeff_write_ctrl_raw(ctl, buf, len); + ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len); return ret; } EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_ctrl); -static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) +static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, + unsigned int off, void *buf, size_t len) { struct cs_dsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - ret = cs_dsp_coeff_base_reg(ctl, ®); + ret = cs_dsp_coeff_base_reg(ctl, ®, off); if (ret) return ret; @@ -806,28 +814,38 @@ static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, void *buf, s /** * cs_dsp_coeff_read_ctrl() - Reads the given coefficient control into the given buffer * @ctl: pointer to coefficient control + * @off: word offset at which data should be read * @buf: the buffer to store to the given control - * @len: the length of the buffer + * @len: the length of the buffer in bytes * * Must be called with pwr_lock held. * * Return: Zero for success, a negative number on error. */ -int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) +int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, + unsigned int off, void *buf, size_t len) { int ret = 0; + if (!ctl) + return -ENOENT; + + lockdep_assert_held(&ctl->dsp->pwr_lock); + + if (len + off * sizeof(u32) > ctl->len) + return -EINVAL; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { if (ctl->enabled && ctl->dsp->running) - return cs_dsp_coeff_read_ctrl_raw(ctl, buf, len); + return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len); else return -EPERM; } else { if (!ctl->flags && ctl->enabled && ctl->dsp->running) - ret = cs_dsp_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); + ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); if (buf != ctl->cache) - memcpy(buf, ctl->cache, len); + memcpy(buf, ctl->cache + off * sizeof(u32), len); } return ret; @@ -851,7 +869,7 @@ static int cs_dsp_coeff_init_control_caches(struct cs_dsp *dsp) * created so we don't need to do anything. */ if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { - ret = cs_dsp_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); + ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); if (ret < 0) return ret; } @@ -869,7 +887,7 @@ static int cs_dsp_coeff_sync_controls(struct cs_dsp *dsp) if (!ctl->enabled) continue; if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { - ret = cs_dsp_coeff_write_ctrl_raw(ctl, ctl->cache, + ret = cs_dsp_coeff_write_ctrl_raw(ctl, 0, ctl->cache, ctl->len); if (ret < 0) return ret; @@ -1159,6 +1177,7 @@ static int cs_dsp_parse_coeff(struct cs_dsp *dsp, return -EINVAL; break; case WMFW_CTL_TYPE_HOSTEVENT: + case WMFW_CTL_TYPE_FWEVENT: ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, WMFW_CTL_FLAG_SYS | WMFW_CTL_FLAG_VOLATILE | @@ -1459,6 +1478,8 @@ struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, in { struct cs_dsp_coeff_ctl *pos, *rslt = NULL; + lockdep_assert_held(&dsp->pwr_lock); + list_for_each_entry(pos, &dsp->ctl_list, list) { if (!pos->subname) continue; @@ -1554,6 +1575,8 @@ struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, { struct cs_dsp_alg_region *alg_region; + lockdep_assert_held(&dsp->pwr_lock); + list_for_each_entry(alg_region, &dsp->alg_regions, list) { if (id == alg_region->alg && type == alg_region->type) return alg_region; @@ -1565,7 +1588,7 @@ EXPORT_SYMBOL_GPL(cs_dsp_find_alg_region); static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, int type, __be32 id, - __be32 base) + __be32 ver, __be32 base) { struct cs_dsp_alg_region *alg_region; @@ -1575,6 +1598,7 @@ static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, alg_region->type = type; alg_region->alg = be32_to_cpu(id); + alg_region->ver = be32_to_cpu(ver); alg_region->base = be32_to_cpu(base); list_add_tail(&alg_region->list, &dsp->alg_regions); @@ -1624,14 +1648,14 @@ static void cs_dsp_parse_wmfw_v3_id_header(struct cs_dsp *dsp, nalgs); } -static int cs_dsp_create_regions(struct cs_dsp *dsp, __be32 id, int nregions, - const int *type, __be32 *base) +static int cs_dsp_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, + int nregions, const int *type, __be32 *base) { struct cs_dsp_alg_region *alg_region; int i; for (i = 0; i < nregions; i++) { - alg_region = cs_dsp_create_region(dsp, type[i], id, base[i]); + alg_region = cs_dsp_create_region(dsp, type[i], id, ver, base[i]); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); } @@ -1666,12 +1690,14 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) cs_dsp_parse_wmfw_id_header(dsp, &adsp1_id.fw, n_algs); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, - adsp1_id.fw.id, adsp1_id.zm); + adsp1_id.fw.id, adsp1_id.fw.ver, + adsp1_id.zm); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, - adsp1_id.fw.id, adsp1_id.dm); + adsp1_id.fw.id, adsp1_id.fw.ver, + adsp1_id.dm); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); @@ -1694,6 +1720,7 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, adsp1_alg[i].alg.id, + adsp1_alg[i].alg.ver, adsp1_alg[i].dm); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1715,6 +1742,7 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, adsp1_alg[i].alg.id, + adsp1_alg[i].alg.ver, adsp1_alg[i].zm); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1767,17 +1795,20 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) cs_dsp_parse_wmfw_id_header(dsp, &adsp2_id.fw, n_algs); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, - adsp2_id.fw.id, adsp2_id.xm); + adsp2_id.fw.id, adsp2_id.fw.ver, + adsp2_id.xm); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, - adsp2_id.fw.id, adsp2_id.ym); + adsp2_id.fw.id, adsp2_id.fw.ver, + adsp2_id.ym); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, - adsp2_id.fw.id, adsp2_id.zm); + adsp2_id.fw.id, adsp2_id.fw.ver, + adsp2_id.zm); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); @@ -1802,6 +1833,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, adsp2_alg[i].alg.id, + adsp2_alg[i].alg.ver, adsp2_alg[i].xm); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1823,6 +1855,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, adsp2_alg[i].alg.id, + adsp2_alg[i].alg.ver, adsp2_alg[i].ym); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1844,6 +1877,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, adsp2_alg[i].alg.id, + adsp2_alg[i].alg.ver, adsp2_alg[i].zm); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1869,7 +1903,7 @@ out: return ret; } -static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, +static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, __be32 xm_base, __be32 ym_base) { static const int types[] = { @@ -1878,7 +1912,7 @@ static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, }; __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; - return cs_dsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases); + return cs_dsp_create_regions(dsp, id, ver, ARRAY_SIZE(types), types, bases); } static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) @@ -1906,7 +1940,7 @@ static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) cs_dsp_parse_wmfw_v3_id_header(dsp, &halo_id.fw, n_algs); - ret = cs_dsp_halo_create_regions(dsp, halo_id.fw.id, + ret = cs_dsp_halo_create_regions(dsp, halo_id.fw.id, halo_id.fw.ver, halo_id.xm_base, halo_id.ym_base); if (ret) return ret; @@ -1930,6 +1964,7 @@ static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) be32_to_cpu(halo_alg[i].ym_base)); ret = cs_dsp_halo_create_regions(dsp, halo_alg[i].alg.id, + halo_alg[i].alg.ver, halo_alg[i].xm_base, halo_alg[i].ym_base); if (ret) @@ -1951,7 +1986,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware const struct cs_dsp_region *mem; struct cs_dsp_alg_region *alg_region; const char *region_name; - int ret, pos, blocks, type, offset, reg; + int ret, pos, blocks, type, offset, reg, version; + char *text = NULL; struct cs_dsp_buf *buf; if (!firmware) @@ -1973,6 +2009,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware switch (be32_to_cpu(hdr->rev) & 0xff) { case 1: + case 2: break; default: cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n", @@ -1995,6 +2032,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware type = le16_to_cpu(blk->type); offset = le16_to_cpu(blk->offset); + version = le32_to_cpu(blk->ver) >> 8; cs_dsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", file, blocks, le32_to_cpu(blk->id), @@ -2008,6 +2046,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware region_name = "Unknown"; switch (type) { case (WMFW_NAME_TEXT << 8): + text = kzalloc(le32_to_cpu(blk->len) + 1, GFP_KERNEL); + break; case (WMFW_INFO_TEXT << 8): case (WMFW_METADATA << 8): break; @@ -2052,6 +2092,16 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware alg_region = cs_dsp_find_alg_region(dsp, type, le32_to_cpu(blk->id)); if (alg_region) { + if (version != alg_region->ver) + cs_dsp_warn(dsp, + "Algorithm coefficient version %d.%d.%d but expected %d.%d.%d\n", + (version >> 16) & 0xFF, + (version >> 8) & 0xFF, + version & 0xFF, + (alg_region->ver >> 16) & 0xFF, + (alg_region->ver >> 8) & 0xFF, + alg_region->ver & 0xFF); + reg = alg_region->base; reg = dsp->ops->region_to_reg(mem, reg); reg += offset; @@ -2067,6 +2117,13 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware break; } + if (text) { + memcpy(text, blk->data, le32_to_cpu(blk->len)); + cs_dsp_info(dsp, "%s: %s\n", dsp->fw_name, text); + kfree(text); + text = NULL; + } + if (reg) { if (le32_to_cpu(blk->len) > firmware->size - pos - sizeof(*blk)) { @@ -2117,6 +2174,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware out_fw: regmap_async_complete(regmap); cs_dsp_buf_free(&buf_list); + kfree(text); return ret; } @@ -2600,6 +2658,12 @@ int cs_dsp_run(struct cs_dsp *dsp) goto err; } + if (dsp->client_ops->pre_run) { + ret = dsp->client_ops->pre_run(dsp); + if (ret) + goto err; + } + /* Sync set controls */ ret = cs_dsp_coeff_sync_controls(dsp); if (ret != 0) @@ -2680,10 +2744,16 @@ EXPORT_SYMBOL_GPL(cs_dsp_stop); static int cs_dsp_halo_start_core(struct cs_dsp *dsp) { - return regmap_update_bits(dsp->regmap, - dsp->base + HALO_CCM_CORE_CONTROL, - HALO_CORE_RESET | HALO_CORE_EN, - HALO_CORE_RESET | HALO_CORE_EN); + int ret; + + ret = regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, + HALO_CORE_RESET | HALO_CORE_EN, + HALO_CORE_RESET | HALO_CORE_EN); + if (ret) + return ret; + + return regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, + HALO_CORE_RESET, 0); } static void cs_dsp_halo_stop_core(struct cs_dsp *dsp) @@ -2789,6 +2859,8 @@ int cs_dsp_read_raw_data_block(struct cs_dsp *dsp, int mem_type, unsigned int me unsigned int reg; int ret; + lockdep_assert_held(&dsp->pwr_lock); + if (!mem) return -EINVAL; @@ -2842,6 +2914,8 @@ int cs_dsp_write_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_ad __be32 val = cpu_to_be32(data & 0x00ffffffu); unsigned int reg; + lockdep_assert_held(&dsp->pwr_lock); + if (!mem) return -EINVAL; diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index ff2b308d8651..11c409cbc88e 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -24,6 +24,7 @@ #include <linux/clk.h> #include <linux/delay.h> +#include <linux/dma/xilinx_dpdma.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/module.h> @@ -1058,14 +1059,18 @@ static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt); /* - * Set slave_id for each DMA channel to indicate they're part of a + * Set pconfig for each DMA channel to indicate they're part of a * video group. */ for (i = 0; i < info->num_planes; i++) { struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; + struct xilinx_dpdma_peripheral_config pconfig = { + .video_group = true, + }; struct dma_slave_config config = { .direction = DMA_MEM_TO_DEV, - .slave_id = 1, + .peripheral_config = &pconfig, + .peripheral_size = sizeof(pconfig), }; dmaengine_slave_config(dma->chan, &config); diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 8c2361e66277..463b707d9e99 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1293,14 +1293,12 @@ static int bcm2835_add_host(struct bcm2835_host *host) host->dma_cfg_tx.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; host->dma_cfg_tx.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_cfg_tx.slave_id = 13; /* DREQ channel */ host->dma_cfg_tx.direction = DMA_MEM_TO_DEV; host->dma_cfg_tx.src_addr = 0; host->dma_cfg_tx.dst_addr = host->phys_addr + SDDATA; host->dma_cfg_rx.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; host->dma_cfg_rx.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_cfg_rx.slave_id = 13; /* DREQ channel */ host->dma_cfg_rx.direction = DMA_DEV_TO_MEM; host->dma_cfg_rx.src_addr = host->phys_addr + SDDATA; host->dma_cfg_rx.dst_addr = 0; diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 04e6f7b26706..7c6efa3b6255 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -6,6 +6,7 @@ #include <linux/clk.h> #include <linux/slab.h> #include <linux/bitops.h> +#include <linux/dma/qcom_adm.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/module.h> @@ -952,6 +953,7 @@ static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, struct dma_async_tx_descriptor *dma_desc; struct scatterlist *sgl; struct dma_slave_config slave_conf; + struct qcom_adm_peripheral_config periph_conf = {}; enum dma_transfer_direction dir_eng; int ret; @@ -983,11 +985,19 @@ static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, if (read) { slave_conf.src_maxburst = 16; slave_conf.src_addr = nandc->base_dma + reg_off; - slave_conf.slave_id = nandc->data_crci; + if (nandc->data_crci) { + periph_conf.crci = nandc->data_crci; + slave_conf.peripheral_config = &periph_conf; + slave_conf.peripheral_size = sizeof(periph_conf); + } } else { slave_conf.dst_maxburst = 16; slave_conf.dst_addr = nandc->base_dma + reg_off; - slave_conf.slave_id = nandc->cmd_crci; + if (nandc->cmd_crci) { + periph_conf.crci = nandc->cmd_crci; + slave_conf.peripheral_config = &periph_conf; + slave_conf.peripheral_size = sizeof(periph_conf); + } } ret = dmaengine_slave_config(nandc->chan, &slave_conf); diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index ca75b14931ec..670cc82d17dc 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -411,12 +411,17 @@ out: return ret; } -static void init_clks(struct platform_device *pdev, struct clk **clk) +static int init_clks(struct platform_device *pdev, struct clk **clk) { int i; - for (i = CLK_NONE + 1; i < CLK_MAX; i++) + for (i = CLK_NONE + 1; i < CLK_MAX; i++) { clk[i] = devm_clk_get(&pdev->dev, clk_names[i]); + if (IS_ERR(clk[i])) + return PTR_ERR(clk[i]); + } + + return 0; } static struct scp *init_scp(struct platform_device *pdev, @@ -426,7 +431,7 @@ static struct scp *init_scp(struct platform_device *pdev, { struct genpd_onecell_data *pd_data; struct resource *res; - int i, j; + int i, j, ret; struct scp *scp; struct clk *clk[CLK_MAX]; @@ -481,7 +486,9 @@ static struct scp *init_scp(struct platform_device *pdev, pd_data->num_domains = num; - init_clks(pdev, clk); + ret = init_clks(pdev, clk); + if (ret) + return ERR_PTR(ret); for (i = 0; i < num; i++) { struct scp_domain *scpd = &scp->domains[i]; diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 4fcc3ba93004..558390af44b6 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1178,9 +1178,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, cdns->pcm.num_bd = config.pcm_bd; cdns->pcm.num_in = config.pcm_in; cdns->pcm.num_out = config.pcm_out; - cdns->pdm.num_bd = config.pdm_bd; - cdns->pdm.num_in = config.pdm_in; - cdns->pdm.num_out = config.pdm_out; /* Allocate PDIs for PCMs */ stream = &cdns->pcm; @@ -1211,32 +1208,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; cdns->num_ports = stream->num_pdi; - /* Allocate PDIs for PDMs */ - stream = &cdns->pdm; - ret = cdns_allocate_pdi(cdns, &stream->bd, - stream->num_bd, offset); - if (ret) - return ret; - - offset += stream->num_bd; - - ret = cdns_allocate_pdi(cdns, &stream->in, - stream->num_in, offset); - if (ret) - return ret; - - offset += stream->num_in; - - ret = cdns_allocate_pdi(cdns, &stream->out, - stream->num_out, offset); - - if (ret) - return ret; - - /* Update total number of PDM PDIs */ - stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; - cdns->num_ports += stream->num_pdi; - return 0; } EXPORT_SYMBOL(sdw_cdns_pdi_init); @@ -1681,7 +1652,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns) EXPORT_SYMBOL(sdw_cdns_probe); int cdns_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, bool pcm, int direction) + void *stream, int direction) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dma_data *dma; @@ -1705,10 +1676,7 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai, if (!dma) return -ENOMEM; - if (pcm) - dma->stream_type = SDW_STREAM_PCM; - else - dma->stream_type = SDW_STREAM_PDM; + dma->stream_type = SDW_STREAM_PCM; dma->bus = &cdns->bus; dma->link_id = cdns->instance; diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index e587aede63bf..595d72c15d97 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -17,7 +17,7 @@ * @h_ch_num: high channel for PDI * @ch_count: total channel count for PDI * @dir: data direction - * @type: stream type, PDM or PCM + * @type: stream type, (only PCM supported) */ struct sdw_cdns_pdi { int num; @@ -62,17 +62,11 @@ struct sdw_cdns_streams { * @pcm_bd: number of bidirectional PCM streams supported * @pcm_in: number of input PCM streams supported * @pcm_out: number of output PCM streams supported - * @pdm_bd: number of bidirectional PDM streams supported - * @pdm_in: number of input PDM streams supported - * @pdm_out: number of output PDM streams supported */ struct sdw_cdns_stream_config { unsigned int pcm_bd; unsigned int pcm_in; unsigned int pcm_out; - unsigned int pdm_bd; - unsigned int pdm_in; - unsigned int pdm_out; }; /** @@ -86,6 +80,7 @@ struct sdw_cdns_stream_config { * @link_id: Master link id * @hw_params: hw_params to be applied in .prepare step * @suspended: status set when suspended, to be used in .prepare + * @paused: status set in .trigger, to be used in suspend */ struct sdw_cdns_dma_data { char *name; @@ -96,6 +91,7 @@ struct sdw_cdns_dma_data { int link_id; struct snd_pcm_hw_params *hw_params; bool suspended; + bool paused; }; /** @@ -109,7 +105,6 @@ struct sdw_cdns_dma_data { * @ports: Data ports * @num_ports: Total number of data ports * @pcm: PCM streams - * @pdm: PDM streams * @registers: Cadence registers * @link_up: Link status * @msg_count: Messages sent on bus @@ -127,7 +122,6 @@ struct sdw_cdns { int num_ports; struct sdw_cdns_streams pcm; - struct sdw_cdns_streams pdm; int pdi_loopback_source; int pdi_loopback_target; @@ -186,7 +180,7 @@ cdns_xfer_msg_defer(struct sdw_bus *bus, int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params); int cdns_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, bool pcm, int direction); + void *stream, int direction); void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string, bool initial_delay, int reset_iterations); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index d082d18e41a9..122f7a29d8ca 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -564,7 +564,7 @@ static void intel_pdi_init(struct sdw_intel *sdw, { void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; - int pcm_cap, pdm_cap; + int pcm_cap; /* PCM Stream Capability */ pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); @@ -575,41 +575,25 @@ static void intel_pdi_init(struct sdw_intel *sdw, dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", config->pcm_bd, config->pcm_in, config->pcm_out); - - /* PDM Stream Capability */ - pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); - - config->pdm_bd = FIELD_GET(SDW_SHIM_PDMSCAP_BSS, pdm_cap); - config->pdm_in = FIELD_GET(SDW_SHIM_PDMSCAP_ISS, pdm_cap); - config->pdm_out = FIELD_GET(SDW_SHIM_PDMSCAP_OSS, pdm_cap); - - dev_dbg(sdw->cdns.dev, "PDM cap bd:%d in:%d out:%d\n", - config->pdm_bd, config->pdm_in, config->pdm_out); } static int -intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) +intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num) { void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int count; - if (pcm) { - count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); + count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); - /* - * WORKAROUND: on all existing Intel controllers, pdi - * number 2 reports channel count as 1 even though it - * supports 8 channels. Performing hardcoding for pdi - * number 2. - */ - if (pdi_num == 2) - count = 7; - - } else { - count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); - count = FIELD_GET(SDW_SHIM_PDMSCAP_CPSS, count); - } + /* + * WORKAROUND: on all existing Intel controllers, pdi + * number 2 reports channel count as 1 even though it + * supports 8 channels. Performing hardcoding for pdi + * number 2. + */ + if (pdi_num == 2) + count = 7; /* zero based values for channel count in register */ count++; @@ -620,12 +604,12 @@ intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) static int intel_pdi_get_ch_update(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi, unsigned int num_pdi, - unsigned int *num_ch, bool pcm) + unsigned int *num_ch) { int i, ch_count = 0; for (i = 0; i < num_pdi; i++) { - pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm); + pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num); ch_count += pdi->ch_count; pdi++; } @@ -635,25 +619,23 @@ static int intel_pdi_get_ch_update(struct sdw_intel *sdw, } static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, - struct sdw_cdns_streams *stream, bool pcm) + struct sdw_cdns_streams *stream) { intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, - &stream->num_ch_bd, pcm); + &stream->num_ch_bd); intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, - &stream->num_ch_in, pcm); + &stream->num_ch_in); intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, - &stream->num_ch_out, pcm); + &stream->num_ch_out); return 0; } static int intel_pdi_ch_update(struct sdw_intel *sdw) { - /* First update PCM streams followed by PDM streams */ - intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true); - intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false); + intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm); return 0; } @@ -711,7 +693,7 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) } static int intel_params_stream(struct sdw_intel *sdw, - struct snd_pcm_substream *substream, + int stream, struct snd_soc_dai *dai, struct snd_pcm_hw_params *hw_params, int link_id, int alh_stream_id) @@ -719,7 +701,7 @@ static int intel_params_stream(struct sdw_intel *sdw, struct sdw_intel_link_res *res = sdw->link_res; struct sdw_intel_stream_params_data params_data; - params_data.substream = substream; + params_data.stream = stream; /* direction */ params_data.dai = dai; params_data.hw_params = hw_params; params_data.link_id = link_id; @@ -732,14 +714,14 @@ static int intel_params_stream(struct sdw_intel *sdw, } static int intel_free_stream(struct sdw_intel *sdw, - struct snd_pcm_substream *substream, + int stream, struct snd_soc_dai *dai, int link_id) { struct sdw_intel_link_res *res = sdw->link_res; struct sdw_intel_stream_free_data free_data; - free_data.substream = substream; + free_data.stream = stream; /* direction */ free_data.dai = dai; free_data.link_id = link_id; @@ -840,7 +822,6 @@ static int intel_hw_params(struct snd_pcm_substream *substream, struct sdw_port_config *pconfig; int ch, dir; int ret; - bool pcm = true; dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) @@ -852,13 +833,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, else dir = SDW_DATA_DIR_TX; - if (dma->stream_type == SDW_STREAM_PDM) - pcm = false; - - if (pcm) - pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); - else - pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id); + pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); if (!pdi) { ret = -EINVAL; @@ -871,12 +846,13 @@ static int intel_hw_params(struct snd_pcm_substream *substream, sdw_cdns_config_stream(cdns, ch, dir, pdi); /* store pdi and hw_params, may be needed in prepare step */ + dma->paused = false; dma->suspended = false; dma->pdi = pdi; dma->hw_params = params; /* Inform DSP about PDI stream number */ - ret = intel_params_stream(sdw, substream, dai, params, + ret = intel_params_stream(sdw, substream->stream, dai, params, sdw->instance, pdi->intel_alh_id); if (ret) @@ -887,12 +863,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, sconfig.frame_rate = params_rate(params); sconfig.type = dma->stream_type; - if (dma->stream_type == SDW_STREAM_PDM) { - sconfig.frame_rate *= 50; - sconfig.bps = 1; - } else { - sconfig.bps = snd_pcm_format_width(params_format(params)); - } + sconfig.bps = snd_pcm_format_width(params_format(params)); /* Port configuration */ pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL); @@ -953,7 +924,7 @@ static int intel_prepare(struct snd_pcm_substream *substream, sdw_cdns_config_stream(cdns, ch, dir, dma->pdi); /* Inform DSP about PDI stream number */ - ret = intel_params_stream(sdw, substream, dai, + ret = intel_params_stream(sdw, substream->stream, dai, dma->hw_params, sdw->instance, dma->pdi->intel_alh_id); @@ -987,7 +958,7 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) return ret; } - ret = intel_free_stream(sdw, substream, dai, sdw->instance); + ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); if (ret < 0) { dev_err(dai->dev, "intel_free_stream: failed %d\n", ret); return ret; @@ -1008,39 +979,10 @@ static void intel_shutdown(struct snd_pcm_substream *substream, pm_runtime_put_autosuspend(cdns->dev); } -static int intel_component_dais_suspend(struct snd_soc_component *component) -{ - struct sdw_cdns_dma_data *dma; - struct snd_soc_dai *dai; - - for_each_component_dais(component, dai) { - /* - * we don't have a .suspend dai_ops, and we don't have access - * to the substream, so let's mark both capture and playback - * DMA contexts as suspended - */ - dma = dai->playback_dma_data; - if (dma) - dma->suspended = true; - - dma = dai->capture_dma_data; - if (dma) - dma->suspended = true; - } - - return 0; -} - static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction) { - return cdns_set_sdw_stream(dai, stream, true, direction); -} - -static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, int direction) -{ - return cdns_set_sdw_stream(dai, stream, false, direction); + return cdns_set_sdw_stream(dai, stream, direction); } static void *intel_get_sdw_stream(struct snd_soc_dai *dai, @@ -1059,24 +1001,100 @@ static void *intel_get_sdw_stream(struct snd_soc_dai *dai, return dma->stream; } -static const struct snd_soc_dai_ops intel_pcm_dai_ops = { - .startup = intel_startup, - .hw_params = intel_hw_params, - .prepare = intel_prepare, - .hw_free = intel_hw_free, - .shutdown = intel_shutdown, - .set_sdw_stream = intel_pcm_set_sdw_stream, - .get_sdw_stream = intel_get_sdw_stream, -}; +static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) +{ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_cdns_dma_data *dma; + int ret = 0; -static const struct snd_soc_dai_ops intel_pdm_dai_ops = { + dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) { + dev_err(dai->dev, "failed to get dma data in %s\n", + __func__); + return -EIO; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_SUSPEND: + + /* + * The .prepare callback is used to deal with xruns and resume operations. + * In the case of xruns, the DMAs and SHIM registers cannot be touched, + * but for resume operations the DMAs and SHIM registers need to be initialized. + * the .trigger callback is used to track the suspend case only. + */ + + dma->suspended = true; + + ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dma->paused = true; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dma->paused = false; + break; + default: + break; + } + + return ret; +} + +static int intel_component_dais_suspend(struct snd_soc_component *component) +{ + struct snd_soc_dai *dai; + + /* + * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core + * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. + * Since the component suspend is called last, we can trap this corner case + * and force the DAIs to release their resources. + */ + for_each_component_dais(component, dai) { + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_cdns_dma_data *dma; + int stream; + int ret; + + dma = dai->playback_dma_data; + stream = SNDRV_PCM_STREAM_PLAYBACK; + if (!dma) { + dma = dai->capture_dma_data; + stream = SNDRV_PCM_STREAM_CAPTURE; + } + + if (!dma) + continue; + + if (dma->suspended) + continue; + + if (dma->paused) { + dma->suspended = true; + + ret = intel_free_stream(sdw, stream, dai, sdw->instance); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .startup = intel_startup, .hw_params = intel_hw_params, .prepare = intel_prepare, .hw_free = intel_hw_free, + .trigger = intel_trigger, .shutdown = intel_shutdown, - .set_sdw_stream = intel_pdm_set_sdw_stream, - .get_sdw_stream = intel_get_sdw_stream, + .set_stream = intel_pcm_set_sdw_stream, + .get_stream = intel_get_sdw_stream, }; static const struct snd_soc_component_driver dai_component = { @@ -1087,7 +1105,7 @@ static const struct snd_soc_component_driver dai_component = { static int intel_create_dai(struct sdw_cdns *cdns, struct snd_soc_dai_driver *dais, enum intel_pdi_type type, - u32 num, u32 off, u32 max_ch, bool pcm) + u32 num, u32 off, u32 max_ch) { int i; @@ -1116,10 +1134,7 @@ static int intel_create_dai(struct sdw_cdns *cdns, dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; } - if (pcm) - dais[i].ops = &intel_pcm_dai_ops; - else - dais[i].ops = &intel_pdm_dai_ops; + dais[i].ops = &intel_pcm_dai_ops; } return 0; @@ -1133,7 +1148,7 @@ static int intel_register_dai(struct sdw_intel *sdw) int num_dai, ret, off = 0; /* DAIs are created based on total number of PDIs supported */ - num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi; + num_dai = cdns->pcm.num_pdi; dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); if (!dais) @@ -1143,39 +1158,19 @@ static int intel_register_dai(struct sdw_intel *sdw) stream = &cdns->pcm; ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in, - off, stream->num_ch_in, true); + off, stream->num_ch_in); if (ret) return ret; off += cdns->pcm.num_in; ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out, - off, stream->num_ch_out, true); + off, stream->num_ch_out); if (ret) return ret; off += cdns->pcm.num_out; ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd, - off, stream->num_ch_bd, true); - if (ret) - return ret; - - /* Create PDM DAIs */ - stream = &cdns->pdm; - off += cdns->pcm.num_bd; - ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pdm.num_in, - off, stream->num_ch_in, false); - if (ret) - return ret; - - off += cdns->pdm.num_in; - ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pdm.num_out, - off, stream->num_ch_out, false); - if (ret) - return ret; - - off += cdns->pdm.num_out; - ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pdm.num_bd, - off, stream->num_ch_bd, false); + off, stream->num_ch_bd); if (ret) return ret; @@ -1549,7 +1544,7 @@ static int __maybe_unused intel_pm_prepare(struct device *dev) struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; u32 clock_stop_quirks; - int ret = 0; + int ret; if (bus->prop.hw_disabled || !sdw->startup_done) { dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index a317bea2d42d..9d42891ac3d6 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1024,8 +1024,8 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, ctrl->sruntime[dai->id] = sruntime; for_each_rtd_codec_dais(rtd, i, codec_dai) { - ret = snd_soc_dai_set_sdw_stream(codec_dai, sruntime, - substream->stream); + ret = snd_soc_dai_set_stream(codec_dai, sruntime, + substream->stream); if (ret < 0 && ret != -ENOTSUPP) { dev_err(dai->dev, "Failed to set sdw stream on %s\n", codec_dai->name); @@ -1051,8 +1051,8 @@ static const struct snd_soc_dai_ops qcom_swrm_pdm_dai_ops = { .hw_free = qcom_swrm_hw_free, .startup = qcom_swrm_startup, .shutdown = qcom_swrm_shutdown, - .set_sdw_stream = qcom_swrm_set_sdw_stream, - .get_sdw_stream = qcom_swrm_get_sdw_stream, + .set_stream = qcom_swrm_set_sdw_stream, + .get_stream = qcom_swrm_get_sdw_stream, }; static const struct snd_soc_component_driver qcom_swrm_dai_component = { diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 5d4f6b308ef7..980f26d49b66 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1863,7 +1863,7 @@ static int set_stream(struct snd_pcm_substream *substream, /* Set stream pointer on all DAIs */ for_each_rtd_dais(rtd, i, dai) { - ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); + ret = snd_soc_dai_set_stream(dai, sdw_stream, substream->stream); if (ret < 0) { dev_err(rtd->dev, "failed to set stream pointer on dai %s\n", dai->name); break; @@ -1934,7 +1934,7 @@ void sdw_shutdown_stream(void *sdw_substream) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 5eb7b61bbb4d..f86433b29260 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -370,7 +370,6 @@ static int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width) cfg.src_addr_width = dma_width; cfg.dst_addr_width = dma_width; /* tx channel */ - cfg.slave_id = pic32s->tx_irq; cfg.direction = DMA_MEM_TO_DEV; ret = dmaengine_slave_config(master->dma_tx, &cfg); if (ret) { @@ -378,7 +377,6 @@ static int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width) return ret; } /* rx channel */ - cfg.slave_id = pic32s->rx_irq; cfg.direction = DMA_DEV_TO_MEM; ret = dmaengine_slave_config(master->dma_rx, &cfg); if (ret) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 489d19274f9a..23c94b927776 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -9,6 +9,7 @@ #include <linux/kernel.h> #include <linux/atomic.h> +#include <linux/dma/qcom_adm.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/module.h> @@ -290,6 +291,7 @@ static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) { struct device *dev = msm_port->uart.dev; struct dma_slave_config conf; + struct qcom_adm_peripheral_config periph_conf = {}; struct msm_dma *dma; u32 crci = 0; int ret; @@ -308,7 +310,11 @@ static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) conf.device_fc = true; conf.dst_addr = base + UARTDM_TF; conf.dst_maxburst = UARTDM_BURST_SIZE; - conf.slave_id = crci; + if (crci) { + conf.peripheral_config = &periph_conf; + conf.peripheral_size = sizeof(periph_conf); + periph_conf.crci = crci; + } ret = dmaengine_slave_config(dma->chan, &conf); if (ret) @@ -333,6 +339,7 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) { struct device *dev = msm_port->uart.dev; struct dma_slave_config conf; + struct qcom_adm_peripheral_config periph_conf = {}; struct msm_dma *dma; u32 crci = 0; int ret; @@ -355,7 +362,11 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) conf.device_fc = true; conf.src_addr = base + UARTDM_RF; conf.src_maxburst = UARTDM_BURST_SIZE; - conf.slave_id = crci; + if (crci) { + conf.peripheral_config = &periph_conf; + conf.peripheral_size = sizeof(periph_conf); + periph_conf.crci = crci; + } ret = dmaengine_slave_config(dma->chan, &conf); if (ret) diff --git a/include/dt-bindings/sound/rt5640.h b/include/dt-bindings/sound/rt5640.h index 154c9b4414f2..655f6946388a 100644 --- a/include/dt-bindings/sound/rt5640.h +++ b/include/dt-bindings/sound/rt5640.h @@ -16,6 +16,7 @@ #define RT5640_JD_SRC_GPIO2 4 #define RT5640_JD_SRC_GPIO3 5 #define RT5640_JD_SRC_GPIO4 6 +#define RT5640_JD_SRC_HDA_HEADER 7 #define RT5640_OVCD_SF_0P5 0 #define RT5640_OVCD_SF_0P75 1 diff --git a/include/dt-bindings/sound/tlv320adc3xxx.h b/include/dt-bindings/sound/tlv320adc3xxx.h new file mode 100644 index 000000000000..ec988439da20 --- /dev/null +++ b/include/dt-bindings/sound/tlv320adc3xxx.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Devicetree bindings definitions for tlv320adc3xxx driver. + * + * Copyright (C) 2021 Axis Communications AB + */ +#ifndef __DT_TLV320ADC3XXX_H +#define __DT_TLV320ADC3XXX_H + +#define ADC3XXX_GPIO_DISABLED 0 /* I/O buffers powered down */ +#define ADC3XXX_GPIO_INPUT 1 /* Various non-GPIO inputs */ +#define ADC3XXX_GPIO_GPI 2 /* General purpose input */ +#define ADC3XXX_GPIO_GPO 3 /* General purpose output */ +#define ADC3XXX_GPIO_CLKOUT 4 /* Source set in reg. CLKOUT_MUX */ +#define ADC3XXX_GPIO_INT1 5 /* INT1 output */ +#define ADC3XXX_GPIO_INT2 6 /* INT2 output */ +/* value 7 is reserved */ +#define ADC3XXX_GPIO_SECONDARY_BCLK 8 /* Codec interface secondary BCLK */ +#define ADC3XXX_GPIO_SECONDARY_WCLK 9 /* Codec interface secondary WCLK */ +#define ADC3XXX_GPIO_ADC_MOD_CLK 10 /* Clock output for digital mics */ +/* values 11-15 reserved */ + +#define ADC3XXX_MICBIAS_OFF 0 /* Micbias pin powered off */ +#define ADC3XXX_MICBIAS_2_0V 1 /* Micbias pin set to 2.0V */ +#define ADC3XXX_MICBIAS_2_5V 2 /* Micbias pin set to 2.5V */ +#define ADC3XXX_MICBIAS_AVDD 3 /* Use AVDD voltage for micbias pin */ + +#endif /* __DT_TLV320ADC3XXX_H */ diff --git a/include/linux/dma/qcom_adm.h b/include/linux/dma/qcom_adm.h new file mode 100644 index 000000000000..af20df674f0c --- /dev/null +++ b/include/linux/dma/qcom_adm.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only +#ifndef __LINUX_DMA_QCOM_ADM_H +#define __LINUX_DMA_QCOM_ADM_H + +#include <linux/types.h> + +struct qcom_adm_peripheral_config { + u32 crci; + u32 mux; +}; + +#endif /* __LINUX_DMA_QCOM_ADM_H */ diff --git a/include/linux/dma/xilinx_dpdma.h b/include/linux/dma/xilinx_dpdma.h new file mode 100644 index 000000000000..83a1377f03f8 --- /dev/null +++ b/include/linux/dma/xilinx_dpdma.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef __LINUX_DMA_XILINX_DPDMA_H +#define __LINUX_DMA_XILINX_DPDMA_H + +#include <linux/types.h> + +struct xilinx_dpdma_peripheral_config { + bool video_group; +}; + +#endif /* __LINUX_DMA_XILINX_DPDMA_H */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9000f3ffce8b..0349b35235e6 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -418,9 +418,6 @@ enum dma_slave_buswidth { * @device_fc: Flow Controller Settings. Only valid for slave channels. Fill * with 'true' if peripheral should be flow controller. Direction will be * selected at Runtime. - * @slave_id: Slave requester id. Only valid for slave channels. The dma - * slave peripheral will have unique id as dma requester which need to be - * pass as slave config. * @peripheral_config: peripheral configuration for programming peripheral * for dmaengine transfer * @peripheral_size: peripheral configuration buffer size @@ -448,7 +445,6 @@ struct dma_slave_config { u32 src_port_window_size; u32 dst_port_window_size; bool device_fc; - unsigned int slave_id; void *peripheral_config; size_t peripheral_size; }; diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 9ad9eaaaa552..38b4da3ddfe4 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -11,6 +11,11 @@ #ifndef __CS_DSP_H #define __CS_DSP_H +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/list.h> +#include <linux/regmap.h> + #define CS_ADSP2_REGION_0 BIT(0) #define CS_ADSP2_REGION_1 BIT(1) #define CS_ADSP2_REGION_2 BIT(2) @@ -49,12 +54,14 @@ struct cs_dsp_region { * struct cs_dsp_alg_region - Describes a logical algorithm region in DSP address space * @list: List node for internal use * @alg: Algorithm id + * @ver: Expected algorithm version * @type: Memory region type * @base: Address of region */ struct cs_dsp_alg_region { struct list_head list; unsigned int alg; + unsigned int ver; int type; unsigned int base; }; @@ -69,8 +76,8 @@ struct cs_dsp_alg_region { * @enabled: Flag indicating whether control is enabled * @list: List node for internal use * @cache: Cached value of the control - * @offset: Offset of control within alg_region - * @len: Length of the cached value + * @offset: Offset of control within alg_region in words + * @len: Length of the cached value in bytes * @set: Flag indicating the value has been written by the user * @flags: Bitfield of WMFW_CTL_FLAG_ control flags defined in wmfw.h * @type: One of the WMFW_CTL_TYPE_ control types defined in wmfw.h @@ -180,7 +187,8 @@ struct cs_dsp { * struct cs_dsp_client_ops - client callbacks * @control_add: Called under the pwr_lock when a control is created * @control_remove: Called under the pwr_lock when a control is destroyed - * @post_run: Called under the pwr_lock by cs_dsp_run() + * @pre_run: Called under the pwr_lock by cs_dsp_run() before the core is started + * @post_run: Called under the pwr_lock by cs_dsp_run() after the core is started * @post_stop: Called under the pwr_lock by cs_dsp_stop() * @watchdog_expired: Called when a watchdog expiry is detected * @@ -190,6 +198,7 @@ struct cs_dsp { struct cs_dsp_client_ops { int (*control_add)(struct cs_dsp_coeff_ctl *ctl); void (*control_remove)(struct cs_dsp_coeff_ctl *ctl); + int (*pre_run)(struct cs_dsp *dsp); int (*post_run)(struct cs_dsp *dsp); void (*post_stop)(struct cs_dsp *dsp); void (*watchdog_expired)(struct cs_dsp *dsp); @@ -223,8 +232,10 @@ void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root); void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp); int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int event_id); -int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, const void *buf, size_t len); -int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len); +int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, unsigned int off, + const void *buf, size_t len); +int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, unsigned int off, + void *buf, size_t len); struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, int type, unsigned int alg); diff --git a/include/linux/firmware/cirrus/wmfw.h b/include/linux/firmware/cirrus/wmfw.h index a19bf7c6fc8b..74e5a4f6c13a 100644 --- a/include/linux/firmware/cirrus/wmfw.h +++ b/include/linux/firmware/cirrus/wmfw.h @@ -29,6 +29,7 @@ #define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */ #define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */ #define WMFW_CTL_TYPE_HOST_BUFFER 0x1002 /* host buffer pointer */ +#define WMFW_CTL_TYPE_FWEVENT 0x1004 /* firmware event control */ struct wmfw_header { char magic[4]; diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 8a463b8fc12a..67e0d3e750b5 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -92,7 +92,7 @@ * firmware. */ struct sdw_intel_stream_params_data { - struct snd_pcm_substream *substream; + int stream; struct snd_soc_dai *dai; struct snd_pcm_hw_params *hw_params; int link_id; @@ -105,7 +105,7 @@ struct sdw_intel_stream_params_data { * firmware. */ struct sdw_intel_stream_free_data { - struct snd_pcm_substream *substream; + int stream; struct snd_soc_dai *dai; int link_id; }; diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 1f1e3c6c9be1..bf7f9a9aeba0 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -10,6 +10,725 @@ #ifndef __CS35L41_H #define __CS35L41_H +#include <linux/regmap.h> + +#define CS35L41_FIRSTREG 0x00000000 +#define CS35L41_LASTREG 0x03804FE8 +#define CS35L41_DEVID 0x00000000 +#define CS35L41_REVID 0x00000004 +#define CS35L41_FABID 0x00000008 +#define CS35L41_RELID 0x0000000C +#define CS35L41_OTPID 0x00000010 +#define CS35L41_SFT_RESET 0x00000020 +#define CS35L41_TEST_KEY_CTL 0x00000040 +#define CS35L41_USER_KEY_CTL 0x00000044 +#define CS35L41_OTP_MEM0 0x00000400 +#define CS35L41_OTP_MEM31 0x0000047C +#define CS35L41_OTP_CTRL0 0x00000500 +#define CS35L41_OTP_CTRL1 0x00000504 +#define CS35L41_OTP_CTRL3 0x00000508 +#define CS35L41_OTP_CTRL4 0x0000050C +#define CS35L41_OTP_CTRL5 0x00000510 +#define CS35L41_OTP_CTRL6 0x00000514 +#define CS35L41_OTP_CTRL7 0x00000518 +#define CS35L41_OTP_CTRL8 0x0000051C +#define CS35L41_PWR_CTRL1 0x00002014 +#define CS35L41_PWR_CTRL2 0x00002018 +#define CS35L41_PWR_CTRL3 0x0000201C +#define CS35L41_CTRL_OVRRIDE 0x00002020 +#define CS35L41_AMP_OUT_MUTE 0x00002024 +#define CS35L41_PROTECT_REL_ERR_IGN 0x00002034 +#define CS35L41_GPIO_PAD_CONTROL 0x0000242C +#define CS35L41_JTAG_CONTROL 0x00002438 +#define CS35L41_PWRMGT_CTL 0x00002900 +#define CS35L41_WAKESRC_CTL 0x00002904 +#define CS35L41_PWRMGT_STS 0x00002908 +#define CS35L41_PLL_CLK_CTRL 0x00002C04 +#define CS35L41_DSP_CLK_CTRL 0x00002C08 +#define CS35L41_GLOBAL_CLK_CTRL 0x00002C0C +#define CS35L41_DATA_FS_SEL 0x00002C10 +#define CS35L41_TST_FS_MON0 0x00002D10 +#define CS35L41_MDSYNC_EN 0x00003400 +#define CS35L41_MDSYNC_TX_ID 0x00003408 +#define CS35L41_MDSYNC_PWR_CTRL 0x0000340C +#define CS35L41_MDSYNC_DATA_TX 0x00003410 +#define CS35L41_MDSYNC_TX_STATUS 0x00003414 +#define CS35L41_MDSYNC_DATA_RX 0x0000341C +#define CS35L41_MDSYNC_RX_STATUS 0x00003420 +#define CS35L41_MDSYNC_ERR_STATUS 0x00003424 +#define CS35L41_MDSYNC_SYNC_PTE2 0x00003528 +#define CS35L41_MDSYNC_SYNC_PTE3 0x0000352C +#define CS35L41_MDSYNC_SYNC_MSM_STATUS 0x0000353C +#define CS35L41_BSTCVRT_VCTRL1 0x00003800 +#define CS35L41_BSTCVRT_VCTRL2 0x00003804 +#define CS35L41_BSTCVRT_PEAK_CUR 0x00003808 +#define CS35L41_BSTCVRT_SFT_RAMP 0x0000380C +#define CS35L41_BSTCVRT_COEFF 0x00003810 +#define CS35L41_BSTCVRT_SLOPE_LBST 0x00003814 +#define CS35L41_BSTCVRT_SW_FREQ 0x00003818 +#define CS35L41_BSTCVRT_DCM_CTRL 0x0000381C +#define CS35L41_BSTCVRT_DCM_MODE_FORCE 0x00003820 +#define CS35L41_BSTCVRT_OVERVOLT_CTRL 0x00003830 +#define CS35L41_VI_VOL_POL 0x00004000 +#define CS35L41_VIMON_SPKMON_RESYNC 0x00004100 +#define CS35L41_DTEMP_WARN_THLD 0x00004220 +#define CS35L41_DTEMP_CFG 0x00004224 +#define CS35L41_DTEMP_EN 0x00004308 +#define CS35L41_VPVBST_FS_SEL 0x00004400 +#define CS35L41_SP_ENABLES 0x00004800 +#define CS35L41_SP_RATE_CTRL 0x00004804 +#define CS35L41_SP_FORMAT 0x00004808 +#define CS35L41_SP_HIZ_CTRL 0x0000480C +#define CS35L41_SP_FRAME_TX_SLOT 0x00004810 +#define CS35L41_SP_FRAME_RX_SLOT 0x00004820 +#define CS35L41_SP_TX_WL 0x00004830 +#define CS35L41_SP_RX_WL 0x00004840 +#define CS35L41_ASP_CONTROL4 0x00004854 +#define CS35L41_DAC_PCM1_SRC 0x00004C00 +#define CS35L41_ASP_TX1_SRC 0x00004C20 +#define CS35L41_ASP_TX2_SRC 0x00004C24 +#define CS35L41_ASP_TX3_SRC 0x00004C28 +#define CS35L41_ASP_TX4_SRC 0x00004C2C +#define CS35L41_DSP1_RX1_SRC 0x00004C40 +#define CS35L41_DSP1_RX2_SRC 0x00004C44 +#define CS35L41_DSP1_RX3_SRC 0x00004C48 +#define CS35L41_DSP1_RX4_SRC 0x00004C4C +#define CS35L41_DSP1_RX5_SRC 0x00004C50 +#define CS35L41_DSP1_RX6_SRC 0x00004C54 +#define CS35L41_DSP1_RX7_SRC 0x00004C58 +#define CS35L41_DSP1_RX8_SRC 0x00004C5C +#define CS35L41_NGATE1_SRC 0x00004C60 +#define CS35L41_NGATE2_SRC 0x00004C64 +#define CS35L41_AMP_DIG_VOL_CTRL 0x00006000 +#define CS35L41_VPBR_CFG 0x00006404 +#define CS35L41_VBBR_CFG 0x00006408 +#define CS35L41_VPBR_STATUS 0x0000640C +#define CS35L41_VBBR_STATUS 0x00006410 +#define CS35L41_OVERTEMP_CFG 0x00006414 +#define CS35L41_AMP_ERR_VOL 0x00006418 +#define CS35L41_VOL_STATUS_TO_DSP 0x00006450 +#define CS35L41_CLASSH_CFG 0x00006800 +#define CS35L41_WKFET_CFG 0x00006804 +#define CS35L41_NG_CFG 0x00006808 +#define CS35L41_AMP_GAIN_CTRL 0x00006C04 +#define CS35L41_DAC_MSM_CFG 0x00007400 +#define CS35L41_IRQ1_CFG 0x00010000 +#define CS35L41_IRQ1_STATUS 0x00010004 +#define CS35L41_IRQ1_STATUS1 0x00010010 +#define CS35L41_IRQ1_STATUS2 0x00010014 +#define CS35L41_IRQ1_STATUS3 0x00010018 +#define CS35L41_IRQ1_STATUS4 0x0001001C +#define CS35L41_IRQ1_RAW_STATUS1 0x00010090 +#define CS35L41_IRQ1_RAW_STATUS2 0x00010094 +#define CS35L41_IRQ1_RAW_STATUS3 0x00010098 +#define CS35L41_IRQ1_RAW_STATUS4 0x0001009C +#define CS35L41_IRQ1_MASK1 0x00010110 +#define CS35L41_IRQ1_MASK2 0x00010114 +#define CS35L41_IRQ1_MASK3 0x00010118 +#define CS35L41_IRQ1_MASK4 0x0001011C +#define CS35L41_IRQ1_FRC1 0x00010190 +#define CS35L41_IRQ1_FRC2 0x00010194 +#define CS35L41_IRQ1_FRC3 0x00010198 +#define CS35L41_IRQ1_FRC4 0x0001019C +#define CS35L41_IRQ1_EDGE1 0x00010210 +#define CS35L41_IRQ1_EDGE4 0x0001021C +#define CS35L41_IRQ1_POL1 0x00010290 +#define CS35L41_IRQ1_POL2 0x00010294 +#define CS35L41_IRQ1_POL3 0x00010298 +#define CS35L41_IRQ1_POL4 0x0001029C +#define CS35L41_IRQ1_DB3 0x00010318 +#define CS35L41_IRQ2_CFG 0x00010800 +#define CS35L41_IRQ2_STATUS 0x00010804 +#define CS35L41_IRQ2_STATUS1 0x00010810 +#define CS35L41_IRQ2_STATUS2 0x00010814 +#define CS35L41_IRQ2_STATUS3 0x00010818 +#define CS35L41_IRQ2_STATUS4 0x0001081C +#define CS35L41_IRQ2_RAW_STATUS1 0x00010890 +#define CS35L41_IRQ2_RAW_STATUS2 0x00010894 +#define CS35L41_IRQ2_RAW_STATUS3 0x00010898 +#define CS35L41_IRQ2_RAW_STATUS4 0x0001089C +#define CS35L41_IRQ2_MASK1 0x00010910 +#define CS35L41_IRQ2_MASK2 0x00010914 +#define CS35L41_IRQ2_MASK3 0x00010918 +#define CS35L41_IRQ2_MASK4 0x0001091C +#define CS35L41_IRQ2_FRC1 0x00010990 +#define CS35L41_IRQ2_FRC2 0x00010994 +#define CS35L41_IRQ2_FRC3 0x00010998 +#define CS35L41_IRQ2_FRC4 0x0001099C +#define CS35L41_IRQ2_EDGE1 0x00010A10 +#define CS35L41_IRQ2_EDGE4 0x00010A1C +#define CS35L41_IRQ2_POL1 0x00010A90 +#define CS35L41_IRQ2_POL2 0x00010A94 +#define CS35L41_IRQ2_POL3 0x00010A98 +#define CS35L41_IRQ2_POL4 0x00010A9C +#define CS35L41_IRQ2_DB3 0x00010B18 +#define CS35L41_GPIO_STATUS1 0x00011000 +#define CS35L41_GPIO1_CTRL1 0x00011008 +#define CS35L41_GPIO2_CTRL1 0x0001100C +#define CS35L41_MIXER_NGATE_CFG 0x00012000 +#define CS35L41_MIXER_NGATE_CH1_CFG 0x00012004 +#define CS35L41_MIXER_NGATE_CH2_CFG 0x00012008 +#define CS35L41_DSP_MBOX_1 0x00013000 +#define CS35L41_DSP_MBOX_2 0x00013004 +#define CS35L41_DSP_MBOX_3 0x00013008 +#define CS35L41_DSP_MBOX_4 0x0001300C +#define CS35L41_DSP_MBOX_5 0x00013010 +#define CS35L41_DSP_MBOX_6 0x00013014 +#define CS35L41_DSP_MBOX_7 0x00013018 +#define CS35L41_DSP_MBOX_8 0x0001301C +#define CS35L41_DSP_VIRT1_MBOX_1 0x00013020 +#define CS35L41_DSP_VIRT1_MBOX_2 0x00013024 +#define CS35L41_DSP_VIRT1_MBOX_3 0x00013028 +#define CS35L41_DSP_VIRT1_MBOX_4 0x0001302C +#define CS35L41_DSP_VIRT1_MBOX_5 0x00013030 +#define CS35L41_DSP_VIRT1_MBOX_6 0x00013034 +#define CS35L41_DSP_VIRT1_MBOX_7 0x00013038 +#define CS35L41_DSP_VIRT1_MBOX_8 0x0001303C +#define CS35L41_DSP_VIRT2_MBOX_1 0x00013040 +#define CS35L41_DSP_VIRT2_MBOX_2 0x00013044 +#define CS35L41_DSP_VIRT2_MBOX_3 0x00013048 +#define CS35L41_DSP_VIRT2_MBOX_4 0x0001304C +#define CS35L41_DSP_VIRT2_MBOX_5 0x00013050 +#define CS35L41_DSP_VIRT2_MBOX_6 0x00013054 +#define CS35L41_DSP_VIRT2_MBOX_7 0x00013058 +#define CS35L41_DSP_VIRT2_MBOX_8 0x0001305C +#define CS35L41_CLOCK_DETECT_1 0x00014000 +#define CS35L41_TIMER1_CONTROL 0x00015000 +#define CS35L41_TIMER1_COUNT_PRESET 0x00015004 +#define CS35L41_TIMER1_START_STOP 0x0001500C +#define CS35L41_TIMER1_STATUS 0x00015010 +#define CS35L41_TIMER1_COUNT_READBACK 0x00015014 +#define CS35L41_TIMER1_DSP_CLK_CFG 0x00015018 +#define CS35L41_TIMER1_DSP_CLK_STATUS 0x0001501C +#define CS35L41_TIMER2_CONTROL 0x00015100 +#define CS35L41_TIMER2_COUNT_PRESET 0x00015104 +#define CS35L41_TIMER2_START_STOP 0x0001510C +#define CS35L41_TIMER2_STATUS 0x00015110 +#define CS35L41_TIMER2_COUNT_READBACK 0x00015114 +#define CS35L41_TIMER2_DSP_CLK_CFG 0x00015118 +#define CS35L41_TIMER2_DSP_CLK_STATUS 0x0001511C +#define CS35L41_DFT_JTAG_CONTROL 0x00016000 +#define CS35L41_DIE_STS1 0x00017040 +#define CS35L41_DIE_STS2 0x00017044 +#define CS35L41_TEMP_CAL1 0x00017048 +#define CS35L41_TEMP_CAL2 0x0001704C +#define CS35L41_DSP1_XMEM_PACK_0 0x02000000 +#define CS35L41_DSP1_XMEM_PACK_3068 0x02002FF0 +#define CS35L41_DSP1_XMEM_UNPACK32_0 0x02400000 +#define CS35L41_DSP1_XMEM_UNPACK32_2046 0x02401FF8 +#define CS35L41_DSP1_TIMESTAMP_COUNT 0x025C0800 +#define CS35L41_DSP1_SYS_ID 0x025E0000 +#define CS35L41_DSP1_SYS_VERSION 0x025E0004 +#define CS35L41_DSP1_SYS_CORE_ID 0x025E0008 +#define CS35L41_DSP1_SYS_AHB_ADDR 0x025E000C +#define CS35L41_DSP1_SYS_XSRAM_SIZE 0x025E0010 +#define CS35L41_DSP1_SYS_YSRAM_SIZE 0x025E0018 +#define CS35L41_DSP1_SYS_PSRAM_SIZE 0x025E0020 +#define CS35L41_DSP1_SYS_PM_BOOT_SIZE 0x025E0028 +#define CS35L41_DSP1_SYS_FEATURES 0x025E002C +#define CS35L41_DSP1_SYS_FIR_FILTERS 0x025E0030 +#define CS35L41_DSP1_SYS_LMS_FILTERS 0x025E0034 +#define CS35L41_DSP1_SYS_XM_BANK_SIZE 0x025E0038 +#define CS35L41_DSP1_SYS_YM_BANK_SIZE 0x025E003C +#define CS35L41_DSP1_SYS_PM_BANK_SIZE 0x025E0040 +#define CS35L41_DSP1_AHBM_WIN0_CTRL0 0x025E2000 +#define CS35L41_DSP1_AHBM_WIN0_CTRL1 0x025E2004 +#define CS35L41_DSP1_AHBM_WIN1_CTRL0 0x025E2008 +#define CS35L41_DSP1_AHBM_WIN1_CTRL1 0x025E200C +#define CS35L41_DSP1_AHBM_WIN2_CTRL0 0x025E2010 +#define CS35L41_DSP1_AHBM_WIN2_CTRL1 0x025E2014 +#define CS35L41_DSP1_AHBM_WIN3_CTRL0 0x025E2018 +#define CS35L41_DSP1_AHBM_WIN3_CTRL1 0x025E201C +#define CS35L41_DSP1_AHBM_WIN4_CTRL0 0x025E2020 +#define CS35L41_DSP1_AHBM_WIN4_CTRL1 0x025E2024 +#define CS35L41_DSP1_AHBM_WIN5_CTRL0 0x025E2028 +#define CS35L41_DSP1_AHBM_WIN5_CTRL1 0x025E202C +#define CS35L41_DSP1_AHBM_WIN6_CTRL0 0x025E2030 +#define CS35L41_DSP1_AHBM_WIN6_CTRL1 0x025E2034 +#define CS35L41_DSP1_AHBM_WIN7_CTRL0 0x025E2038 +#define CS35L41_DSP1_AHBM_WIN7_CTRL1 0x025E203C +#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL0 0x025E2040 +#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL1 0x025E2044 +#define CS35L41_DSP1_XMEM_UNPACK24_0 0x02800000 +#define CS35L41_DSP1_XMEM_UNPACK24_4093 0x02803FF4 +#define CS35L41_DSP1_CTRL_BASE 0x02B80000 +#define CS35L41_DSP1_CORE_SOFT_RESET 0x02B80010 +#define CS35L41_DSP1_DEBUG 0x02B80040 +#define CS35L41_DSP1_TIMER_CTRL 0x02B80048 +#define CS35L41_DSP1_STREAM_ARB_CTRL 0x02B80050 +#define CS35L41_DSP1_RX1_RATE 0x02B80080 +#define CS35L41_DSP1_RX2_RATE 0x02B80088 +#define CS35L41_DSP1_RX3_RATE 0x02B80090 +#define CS35L41_DSP1_RX4_RATE 0x02B80098 +#define CS35L41_DSP1_RX5_RATE 0x02B800A0 +#define CS35L41_DSP1_RX6_RATE 0x02B800A8 +#define CS35L41_DSP1_RX7_RATE 0x02B800B0 +#define CS35L41_DSP1_RX8_RATE 0x02B800B8 +#define CS35L41_DSP1_TX1_RATE 0x02B80280 +#define CS35L41_DSP1_TX2_RATE 0x02B80288 +#define CS35L41_DSP1_TX3_RATE 0x02B80290 +#define CS35L41_DSP1_TX4_RATE 0x02B80298 +#define CS35L41_DSP1_TX5_RATE 0x02B802A0 +#define CS35L41_DSP1_TX6_RATE 0x02B802A8 +#define CS35L41_DSP1_TX7_RATE 0x02B802B0 +#define CS35L41_DSP1_TX8_RATE 0x02B802B8 +#define CS35L41_DSP1_NMI_CTRL1 0x02B80480 +#define CS35L41_DSP1_NMI_CTRL2 0x02B80488 +#define CS35L41_DSP1_NMI_CTRL3 0x02B80490 +#define CS35L41_DSP1_NMI_CTRL4 0x02B80498 +#define CS35L41_DSP1_NMI_CTRL5 0x02B804A0 +#define CS35L41_DSP1_NMI_CTRL6 0x02B804A8 +#define CS35L41_DSP1_NMI_CTRL7 0x02B804B0 +#define CS35L41_DSP1_NMI_CTRL8 0x02B804B8 +#define CS35L41_DSP1_RESUME_CTRL 0x02B80500 +#define CS35L41_DSP1_IRQ1_CTRL 0x02B80508 +#define CS35L41_DSP1_IRQ2_CTRL 0x02B80510 +#define CS35L41_DSP1_IRQ3_CTRL 0x02B80518 +#define CS35L41_DSP1_IRQ4_CTRL 0x02B80520 +#define CS35L41_DSP1_IRQ5_CTRL 0x02B80528 +#define CS35L41_DSP1_IRQ6_CTRL 0x02B80530 +#define CS35L41_DSP1_IRQ7_CTRL 0x02B80538 +#define CS35L41_DSP1_IRQ8_CTRL 0x02B80540 +#define CS35L41_DSP1_IRQ9_CTRL 0x02B80548 +#define CS35L41_DSP1_IRQ10_CTRL 0x02B80550 +#define CS35L41_DSP1_IRQ11_CTRL 0x02B80558 +#define CS35L41_DSP1_IRQ12_CTRL 0x02B80560 +#define CS35L41_DSP1_IRQ13_CTRL 0x02B80568 +#define CS35L41_DSP1_IRQ14_CTRL 0x02B80570 +#define CS35L41_DSP1_IRQ15_CTRL 0x02B80578 +#define CS35L41_DSP1_IRQ16_CTRL 0x02B80580 +#define CS35L41_DSP1_IRQ17_CTRL 0x02B80588 +#define CS35L41_DSP1_IRQ18_CTRL 0x02B80590 +#define CS35L41_DSP1_IRQ19_CTRL 0x02B80598 +#define CS35L41_DSP1_IRQ20_CTRL 0x02B805A0 +#define CS35L41_DSP1_IRQ21_CTRL 0x02B805A8 +#define CS35L41_DSP1_IRQ22_CTRL 0x02B805B0 +#define CS35L41_DSP1_IRQ23_CTRL 0x02B805B8 +#define CS35L41_DSP1_SCRATCH1 0x02B805C0 +#define CS35L41_DSP1_SCRATCH2 0x02B805C8 +#define CS35L41_DSP1_SCRATCH3 0x02B805D0 +#define CS35L41_DSP1_SCRATCH4 0x02B805D8 +#define CS35L41_DSP1_CCM_CORE_CTRL 0x02BC1000 +#define CS35L41_DSP1_CCM_CLK_OVERRIDE 0x02BC1008 +#define CS35L41_DSP1_XM_MSTR_EN 0x02BC2000 +#define CS35L41_DSP1_XM_CORE_PRI 0x02BC2008 +#define CS35L41_DSP1_XM_AHB_PACK_PL_PRI 0x02BC2010 +#define CS35L41_DSP1_XM_AHB_UP_PL_PRI 0x02BC2018 +#define CS35L41_DSP1_XM_ACCEL_PL0_PRI 0x02BC2020 +#define CS35L41_DSP1_XM_NPL0_PRI 0x02BC2078 +#define CS35L41_DSP1_YM_MSTR_EN 0x02BC20C0 +#define CS35L41_DSP1_YM_CORE_PRI 0x02BC20C8 +#define CS35L41_DSP1_YM_AHB_PACK_PL_PRI 0x02BC20D0 +#define CS35L41_DSP1_YM_AHB_UP_PL_PRI 0x02BC20D8 +#define CS35L41_DSP1_YM_ACCEL_PL0_PRI 0x02BC20E0 +#define CS35L41_DSP1_YM_NPL0_PRI 0x02BC2138 +#define CS35L41_DSP1_PM_MSTR_EN 0x02BC2180 +#define CS35L41_DSP1_PM_PATCH0_ADDR 0x02BC2188 +#define CS35L41_DSP1_PM_PATCH0_EN 0x02BC218C +#define CS35L41_DSP1_PM_PATCH0_DATA_LO 0x02BC2190 +#define CS35L41_DSP1_PM_PATCH0_DATA_HI 0x02BC2194 +#define CS35L41_DSP1_PM_PATCH1_ADDR 0x02BC2198 +#define CS35L41_DSP1_PM_PATCH1_EN 0x02BC219C +#define CS35L41_DSP1_PM_PATCH1_DATA_LO 0x02BC21A0 +#define CS35L41_DSP1_PM_PATCH1_DATA_HI 0x02BC21A4 +#define CS35L41_DSP1_PM_PATCH2_ADDR 0x02BC21A8 +#define CS35L41_DSP1_PM_PATCH2_EN 0x02BC21AC +#define CS35L41_DSP1_PM_PATCH2_DATA_LO 0x02BC21B0 +#define CS35L41_DSP1_PM_PATCH2_DATA_HI 0x02BC21B4 +#define CS35L41_DSP1_PM_PATCH3_ADDR 0x02BC21B8 +#define CS35L41_DSP1_PM_PATCH3_EN 0x02BC21BC +#define CS35L41_DSP1_PM_PATCH3_DATA_LO 0x02BC21C0 +#define CS35L41_DSP1_PM_PATCH3_DATA_HI 0x02BC21C4 +#define CS35L41_DSP1_PM_PATCH4_ADDR 0x02BC21C8 +#define CS35L41_DSP1_PM_PATCH4_EN 0x02BC21CC +#define CS35L41_DSP1_PM_PATCH4_DATA_LO 0x02BC21D0 +#define CS35L41_DSP1_PM_PATCH4_DATA_HI 0x02BC21D4 +#define CS35L41_DSP1_PM_PATCH5_ADDR 0x02BC21D8 +#define CS35L41_DSP1_PM_PATCH5_EN 0x02BC21DC +#define CS35L41_DSP1_PM_PATCH5_DATA_LO 0x02BC21E0 +#define CS35L41_DSP1_PM_PATCH5_DATA_HI 0x02BC21E4 +#define CS35L41_DSP1_PM_PATCH6_ADDR 0x02BC21E8 +#define CS35L41_DSP1_PM_PATCH6_EN 0x02BC21EC +#define CS35L41_DSP1_PM_PATCH6_DATA_LO 0x02BC21F0 +#define CS35L41_DSP1_PM_PATCH6_DATA_HI 0x02BC21F4 +#define CS35L41_DSP1_PM_PATCH7_ADDR 0x02BC21F8 +#define CS35L41_DSP1_PM_PATCH7_EN 0x02BC21FC +#define CS35L41_DSP1_PM_PATCH7_DATA_LO 0x02BC2200 +#define CS35L41_DSP1_PM_PATCH7_DATA_HI 0x02BC2204 +#define CS35L41_DSP1_MPU_XM_ACCESS0 0x02BC3000 +#define CS35L41_DSP1_MPU_YM_ACCESS0 0x02BC3004 +#define CS35L41_DSP1_MPU_WNDW_ACCESS0 0x02BC3008 +#define CS35L41_DSP1_MPU_XREG_ACCESS0 0x02BC300C +#define CS35L41_DSP1_MPU_YREG_ACCESS0 0x02BC3014 +#define CS35L41_DSP1_MPU_XM_ACCESS1 0x02BC3018 +#define CS35L41_DSP1_MPU_YM_ACCESS1 0x02BC301C +#define CS35L41_DSP1_MPU_WNDW_ACCESS1 0x02BC3020 +#define CS35L41_DSP1_MPU_XREG_ACCESS1 0x02BC3024 +#define CS35L41_DSP1_MPU_YREG_ACCESS1 0x02BC302C +#define CS35L41_DSP1_MPU_XM_ACCESS2 0x02BC3030 +#define CS35L41_DSP1_MPU_YM_ACCESS2 0x02BC3034 +#define CS35L41_DSP1_MPU_WNDW_ACCESS2 0x02BC3038 +#define CS35L41_DSP1_MPU_XREG_ACCESS2 0x02BC303C +#define CS35L41_DSP1_MPU_YREG_ACCESS2 0x02BC3044 +#define CS35L41_DSP1_MPU_XM_ACCESS3 0x02BC3048 +#define CS35L41_DSP1_MPU_YM_ACCESS3 0x02BC304C +#define CS35L41_DSP1_MPU_WNDW_ACCESS3 0x02BC3050 +#define CS35L41_DSP1_MPU_XREG_ACCESS3 0x02BC3054 +#define CS35L41_DSP1_MPU_YREG_ACCESS3 0x02BC305C +#define CS35L41_DSP1_MPU_XM_VIO_ADDR 0x02BC3100 +#define CS35L41_DSP1_MPU_XM_VIO_STATUS 0x02BC3104 +#define CS35L41_DSP1_MPU_YM_VIO_ADDR 0x02BC3108 +#define CS35L41_DSP1_MPU_YM_VIO_STATUS 0x02BC310C +#define CS35L41_DSP1_MPU_PM_VIO_ADDR 0x02BC3110 +#define CS35L41_DSP1_MPU_PM_VIO_STATUS 0x02BC3114 +#define CS35L41_DSP1_MPU_LOCK_CONFIG 0x02BC3140 +#define CS35L41_DSP1_MPU_WDT_RST_CTRL 0x02BC3180 +#define CS35L41_DSP1_STRMARB_MSTR0_CFG0 0x02BC5000 +#define CS35L41_DSP1_STRMARB_MSTR0_CFG1 0x02BC5004 +#define CS35L41_DSP1_STRMARB_MSTR0_CFG2 0x02BC5008 +#define CS35L41_DSP1_STRMARB_MSTR1_CFG0 0x02BC5010 +#define CS35L41_DSP1_STRMARB_MSTR1_CFG1 0x02BC5014 +#define CS35L41_DSP1_STRMARB_MSTR1_CFG2 0x02BC5018 +#define CS35L41_DSP1_STRMARB_MSTR2_CFG0 0x02BC5020 +#define CS35L41_DSP1_STRMARB_MSTR2_CFG1 0x02BC5024 +#define CS35L41_DSP1_STRMARB_MSTR2_CFG2 0x02BC5028 +#define CS35L41_DSP1_STRMARB_MSTR3_CFG0 0x02BC5030 +#define CS35L41_DSP1_STRMARB_MSTR3_CFG1 0x02BC5034 +#define CS35L41_DSP1_STRMARB_MSTR3_CFG2 0x02BC5038 +#define CS35L41_DSP1_STRMARB_MSTR4_CFG0 0x02BC5040 +#define CS35L41_DSP1_STRMARB_MSTR4_CFG1 0x02BC5044 +#define CS35L41_DSP1_STRMARB_MSTR4_CFG2 0x02BC5048 +#define CS35L41_DSP1_STRMARB_MSTR5_CFG0 0x02BC5050 +#define CS35L41_DSP1_STRMARB_MSTR5_CFG1 0x02BC5054 +#define CS35L41_DSP1_STRMARB_MSTR5_CFG2 0x02BC5058 +#define CS35L41_DSP1_STRMARB_MSTR6_CFG0 0x02BC5060 +#define CS35L41_DSP1_STRMARB_MSTR6_CFG1 0x02BC5064 +#define CS35L41_DSP1_STRMARB_MSTR6_CFG2 0x02BC5068 +#define CS35L41_DSP1_STRMARB_MSTR7_CFG0 0x02BC5070 +#define CS35L41_DSP1_STRMARB_MSTR7_CFG1 0x02BC5074 +#define CS35L41_DSP1_STRMARB_MSTR7_CFG2 0x02BC5078 +#define CS35L41_DSP1_STRMARB_TX0_CFG0 0x02BC5200 +#define CS35L41_DSP1_STRMARB_TX0_CFG1 0x02BC5204 +#define CS35L41_DSP1_STRMARB_TX1_CFG0 0x02BC5208 +#define CS35L41_DSP1_STRMARB_TX1_CFG1 0x02BC520C +#define CS35L41_DSP1_STRMARB_TX2_CFG0 0x02BC5210 +#define CS35L41_DSP1_STRMARB_TX2_CFG1 0x02BC5214 +#define CS35L41_DSP1_STRMARB_TX3_CFG0 0x02BC5218 +#define CS35L41_DSP1_STRMARB_TX3_CFG1 0x02BC521C +#define CS35L41_DSP1_STRMARB_TX4_CFG0 0x02BC5220 +#define CS35L41_DSP1_STRMARB_TX4_CFG1 0x02BC5224 +#define CS35L41_DSP1_STRMARB_TX5_CFG0 0x02BC5228 +#define CS35L41_DSP1_STRMARB_TX5_CFG1 0x02BC522C +#define CS35L41_DSP1_STRMARB_TX6_CFG0 0x02BC5230 +#define CS35L41_DSP1_STRMARB_TX6_CFG1 0x02BC5234 +#define CS35L41_DSP1_STRMARB_TX7_CFG0 0x02BC5238 +#define CS35L41_DSP1_STRMARB_TX7_CFG1 0x02BC523C +#define CS35L41_DSP1_STRMARB_RX0_CFG0 0x02BC5400 +#define CS35L41_DSP1_STRMARB_RX0_CFG1 0x02BC5404 +#define CS35L41_DSP1_STRMARB_RX1_CFG0 0x02BC5408 +#define CS35L41_DSP1_STRMARB_RX1_CFG1 0x02BC540C +#define CS35L41_DSP1_STRMARB_RX2_CFG0 0x02BC5410 +#define CS35L41_DSP1_STRMARB_RX2_CFG1 0x02BC5414 +#define CS35L41_DSP1_STRMARB_RX3_CFG0 0x02BC5418 +#define CS35L41_DSP1_STRMARB_RX3_CFG1 0x02BC541C +#define CS35L41_DSP1_STRMARB_RX4_CFG0 0x02BC5420 +#define CS35L41_DSP1_STRMARB_RX4_CFG1 0x02BC5424 +#define CS35L41_DSP1_STRMARB_RX5_CFG0 0x02BC5428 +#define CS35L41_DSP1_STRMARB_RX5_CFG1 0x02BC542C +#define CS35L41_DSP1_STRMARB_RX6_CFG0 0x02BC5430 +#define CS35L41_DSP1_STRMARB_RX6_CFG1 0x02BC5434 +#define CS35L41_DSP1_STRMARB_RX7_CFG0 0x02BC5438 +#define CS35L41_DSP1_STRMARB_RX7_CFG1 0x02BC543C +#define CS35L41_DSP1_STRMARB_IRQ0_CFG0 0x02BC5600 +#define CS35L41_DSP1_STRMARB_IRQ0_CFG1 0x02BC5604 +#define CS35L41_DSP1_STRMARB_IRQ0_CFG2 0x02BC5608 +#define CS35L41_DSP1_STRMARB_IRQ1_CFG0 0x02BC5610 +#define CS35L41_DSP1_STRMARB_IRQ1_CFG1 0x02BC5614 +#define CS35L41_DSP1_STRMARB_IRQ1_CFG2 0x02BC5618 +#define CS35L41_DSP1_STRMARB_IRQ2_CFG0 0x02BC5620 +#define CS35L41_DSP1_STRMARB_IRQ2_CFG1 0x02BC5624 +#define CS35L41_DSP1_STRMARB_IRQ2_CFG2 0x02BC5628 +#define CS35L41_DSP1_STRMARB_IRQ3_CFG0 0x02BC5630 +#define CS35L41_DSP1_STRMARB_IRQ3_CFG1 0x02BC5634 +#define CS35L41_DSP1_STRMARB_IRQ3_CFG2 0x02BC5638 +#define CS35L41_DSP1_STRMARB_IRQ4_CFG0 0x02BC5640 +#define CS35L41_DSP1_STRMARB_IRQ4_CFG1 0x02BC5644 +#define CS35L41_DSP1_STRMARB_IRQ4_CFG2 0x02BC5648 +#define CS35L41_DSP1_STRMARB_IRQ5_CFG0 0x02BC5650 +#define CS35L41_DSP1_STRMARB_IRQ5_CFG1 0x02BC5654 +#define CS35L41_DSP1_STRMARB_IRQ5_CFG2 0x02BC5658 +#define CS35L41_DSP1_STRMARB_IRQ6_CFG0 0x02BC5660 +#define CS35L41_DSP1_STRMARB_IRQ6_CFG1 0x02BC5664 +#define CS35L41_DSP1_STRMARB_IRQ6_CFG2 0x02BC5668 +#define CS35L41_DSP1_STRMARB_IRQ7_CFG0 0x02BC5670 +#define CS35L41_DSP1_STRMARB_IRQ7_CFG1 0x02BC5674 +#define CS35L41_DSP1_STRMARB_IRQ7_CFG2 0x02BC5678 +#define CS35L41_DSP1_STRMARB_RESYNC_MSK 0x02BC5A00 +#define CS35L41_DSP1_STRMARB_ERR_STATUS 0x02BC5A08 +#define CS35L41_DSP1_INTPCTL_RES_STATIC 0x02BC6000 +#define CS35L41_DSP1_INTPCTL_RES_DYN 0x02BC6004 +#define CS35L41_DSP1_INTPCTL_NMI_CTRL 0x02BC6008 +#define CS35L41_DSP1_INTPCTL_IRQ_INV 0x02BC6010 +#define CS35L41_DSP1_INTPCTL_IRQ_MODE 0x02BC6014 +#define CS35L41_DSP1_INTPCTL_IRQ_EN 0x02BC6018 +#define CS35L41_DSP1_INTPCTL_IRQ_MSK 0x02BC601C +#define CS35L41_DSP1_INTPCTL_IRQ_FLUSH 0x02BC6020 +#define CS35L41_DSP1_INTPCTL_IRQ_MSKCLR 0x02BC6024 +#define CS35L41_DSP1_INTPCTL_IRQ_FRC 0x02BC6028 +#define CS35L41_DSP1_INTPCTL_IRQ_MSKSET 0x02BC602C +#define CS35L41_DSP1_INTPCTL_IRQ_ERR 0x02BC6030 +#define CS35L41_DSP1_INTPCTL_IRQ_PEND 0x02BC6034 +#define CS35L41_DSP1_INTPCTL_IRQ_GEN 0x02BC6038 +#define CS35L41_DSP1_INTPCTL_TESTBITS 0x02BC6040 +#define CS35L41_DSP1_WDT_CONTROL 0x02BC7000 +#define CS35L41_DSP1_WDT_STATUS 0x02BC7008 +#define CS35L41_DSP1_YMEM_PACK_0 0x02C00000 +#define CS35L41_DSP1_YMEM_PACK_1532 0x02C017F0 +#define CS35L41_DSP1_YMEM_UNPACK32_0 0x03000000 +#define CS35L41_DSP1_YMEM_UNPACK32_1022 0x03000FF8 +#define CS35L41_DSP1_YMEM_UNPACK24_0 0x03400000 +#define CS35L41_DSP1_YMEM_UNPACK24_2045 0x03401FF4 +#define CS35L41_DSP1_PMEM_0 0x03800000 +#define CS35L41_DSP1_PMEM_5114 0x03804FE8 + +/*test regs for emulation bringup*/ +#define CS35L41_PLL_OVR 0x00003018 +#define CS35L41_BST_TEST_DUTY 0x00003900 +#define CS35L41_DIGPWM_IOCTRL 0x0000706C + +/*registers populated by OTP*/ +#define CS35L41_OTP_TRIM_1 0x0000208c +#define CS35L41_OTP_TRIM_2 0x00002090 +#define CS35L41_OTP_TRIM_3 0x00003010 +#define CS35L41_OTP_TRIM_4 0x0000300C +#define CS35L41_OTP_TRIM_5 0x0000394C +#define CS35L41_OTP_TRIM_6 0x00003950 +#define CS35L41_OTP_TRIM_7 0x00003954 +#define CS35L41_OTP_TRIM_8 0x00003958 +#define CS35L41_OTP_TRIM_9 0x0000395C +#define CS35L41_OTP_TRIM_10 0x0000416C +#define CS35L41_OTP_TRIM_11 0x00004160 +#define CS35L41_OTP_TRIM_12 0x00004170 +#define CS35L41_OTP_TRIM_13 0x00004360 +#define CS35L41_OTP_TRIM_14 0x00004448 +#define CS35L41_OTP_TRIM_15 0x0000444C +#define CS35L41_OTP_TRIM_16 0x00006E30 +#define CS35L41_OTP_TRIM_17 0x00006E34 +#define CS35L41_OTP_TRIM_18 0x00006E38 +#define CS35L41_OTP_TRIM_19 0x00006E3C +#define CS35L41_OTP_TRIM_20 0x00006E40 +#define CS35L41_OTP_TRIM_21 0x00006E44 +#define CS35L41_OTP_TRIM_22 0x00006E48 +#define CS35L41_OTP_TRIM_23 0x00006E4C +#define CS35L41_OTP_TRIM_24 0x00006E50 +#define CS35L41_OTP_TRIM_25 0x00006E54 +#define CS35L41_OTP_TRIM_26 0x00006E58 +#define CS35L41_OTP_TRIM_27 0x00006E5C +#define CS35L41_OTP_TRIM_28 0x00006E60 +#define CS35L41_OTP_TRIM_29 0x00006E64 +#define CS35L41_OTP_TRIM_30 0x00007418 +#define CS35L41_OTP_TRIM_31 0x0000741C +#define CS35L41_OTP_TRIM_32 0x00007434 +#define CS35L41_OTP_TRIM_33 0x00007068 +#define CS35L41_OTP_TRIM_34 0x0000410C +#define CS35L41_OTP_TRIM_35 0x0000400C +#define CS35L41_OTP_TRIM_36 0x00002030 + +#define CS35L41_MAX_CACHE_REG 36 +#define CS35L41_OTP_SIZE_WORDS 32 +#define CS35L41_NUM_OTP_ELEM 100 + +#define CS35L41_VALID_PDATA 0x80000000 +#define CS35L41_NUM_SUPPLIES 2 + +#define CS35L41_SCLK_MSTR_MASK 0x10 +#define CS35L41_SCLK_MSTR_SHIFT 4 +#define CS35L41_LRCLK_MSTR_MASK 0x01 +#define CS35L41_LRCLK_MSTR_SHIFT 0 +#define CS35L41_SCLK_INV_MASK 0x40 +#define CS35L41_SCLK_INV_SHIFT 6 +#define CS35L41_LRCLK_INV_MASK 0x04 +#define CS35L41_LRCLK_INV_SHIFT 2 +#define CS35L41_SCLK_FRC_MASK 0x20 +#define CS35L41_SCLK_FRC_SHIFT 5 +#define CS35L41_LRCLK_FRC_MASK 0x02 +#define CS35L41_LRCLK_FRC_SHIFT 1 + +#define CS35L41_AMP_GAIN_PCM_MASK 0x3E0 +#define CS35L41_AMP_GAIN_ZC_MASK 0x0400 +#define CS35L41_AMP_GAIN_ZC_SHIFT 10 + +#define CS35L41_BST_CTL_MASK 0xFF +#define CS35L41_BST_CTL_SEL_MASK 0x03 +#define CS35L41_BST_CTL_SEL_REG 0x00 +#define CS35L41_BST_CTL_SEL_CLASSH 0x01 +#define CS35L41_BST_IPK_MASK 0x7F +#define CS35L41_BST_IPK_SHIFT 0 +#define CS35L41_BST_LIM_MASK 0x4 +#define CS35L41_BST_LIM_SHIFT 2 +#define CS35L41_BST_K1_MASK 0x000000FF +#define CS35L41_BST_K1_SHIFT 0 +#define CS35L41_BST_K2_MASK 0x0000FF00 +#define CS35L41_BST_K2_SHIFT 8 +#define CS35L41_BST_SLOPE_MASK 0x0000FF00 +#define CS35L41_BST_SLOPE_SHIFT 8 +#define CS35L41_BST_LBST_VAL_MASK 0x00000003 +#define CS35L41_BST_LBST_VAL_SHIFT 0 + +#define CS35L41_TEMP_THLD_MASK 0x03 +#define CS35L41_VMON_IMON_VOL_MASK 0x07FF07FF +#define CS35L41_PDM_MODE_MASK 0x01 +#define CS35L41_PDM_MODE_SHIFT 0 + +#define CS35L41_CH_MEM_DEPTH_MASK 0x07 +#define CS35L41_CH_MEM_DEPTH_SHIFT 0 +#define CS35L41_CH_HDRM_CTL_MASK 0x007F0000 +#define CS35L41_CH_HDRM_CTL_SHIFT 16 +#define CS35L41_CH_REL_RATE_MASK 0xFF00 +#define CS35L41_CH_REL_RATE_SHIFT 8 +#define CS35L41_CH_WKFET_DLY_MASK 0x001C +#define CS35L41_CH_WKFET_DLY_SHIFT 2 +#define CS35L41_CH_WKFET_THLD_MASK 0x0F00 +#define CS35L41_CH_WKFET_THLD_SHIFT 8 + +#define CS35L41_HW_NG_SEL_MASK 0x3F00 +#define CS35L41_HW_NG_SEL_SHIFT 8 +#define CS35L41_HW_NG_DLY_MASK 0x0070 +#define CS35L41_HW_NG_DLY_SHIFT 4 +#define CS35L41_HW_NG_THLD_MASK 0x0007 +#define CS35L41_HW_NG_THLD_SHIFT 0 + +#define CS35L41_DSP_NG_ENABLE_MASK 0x00010000 +#define CS35L41_DSP_NG_ENABLE_SHIFT 16 +#define CS35L41_DSP_NG_THLD_MASK 0x7 +#define CS35L41_DSP_NG_THLD_SHIFT 0 +#define CS35L41_DSP_NG_DELAY_MASK 0x0F00 +#define CS35L41_DSP_NG_DELAY_SHIFT 8 + +#define CS35L41_ASP_FMT_MASK 0x0700 +#define CS35L41_ASP_FMT_SHIFT 8 +#define CS35L41_ASP_DOUT_HIZ_MASK 0x03 +#define CS35L41_ASP_DOUT_HIZ_SHIFT 0 +#define CS35L41_ASP_WIDTH_16 0x10 +#define CS35L41_ASP_WIDTH_24 0x18 +#define CS35L41_ASP_WIDTH_32 0x20 +#define CS35L41_ASP_WIDTH_TX_MASK 0xFF0000 +#define CS35L41_ASP_WIDTH_TX_SHIFT 16 +#define CS35L41_ASP_WIDTH_RX_MASK 0xFF000000 +#define CS35L41_ASP_WIDTH_RX_SHIFT 24 +#define CS35L41_ASP_RX1_SLOT_MASK 0x3F +#define CS35L41_ASP_RX1_SLOT_SHIFT 0 +#define CS35L41_ASP_RX2_SLOT_MASK 0x3F00 +#define CS35L41_ASP_RX2_SLOT_SHIFT 8 +#define CS35L41_ASP_RX_WL_MASK 0x3F +#define CS35L41_ASP_TX_WL_MASK 0x3F +#define CS35L41_ASP_RX_WL_SHIFT 0 +#define CS35L41_ASP_TX_WL_SHIFT 0 +#define CS35L41_ASP_SOURCE_MASK 0x7F + +#define CS35L41_INPUT_SRC_ASPRX1 0x08 +#define CS35L41_INPUT_SRC_ASPRX2 0x09 +#define CS35L41_INPUT_SRC_VMON 0x18 +#define CS35L41_INPUT_SRC_IMON 0x19 +#define CS35L41_INPUT_SRC_CLASSH 0x21 +#define CS35L41_INPUT_SRC_VPMON 0x28 +#define CS35L41_INPUT_SRC_VBSTMON 0x29 +#define CS35L41_INPUT_SRC_TEMPMON 0x3A +#define CS35L41_INPUT_SRC_RSVD 0x3B +#define CS35L41_INPUT_DSP_TX1 0x32 +#define CS35L41_INPUT_DSP_TX2 0x33 + +#define CS35L41_WR_PEND_STS_MASK 0x2 + +#define CS35L41_PLL_CLK_SEL_MASK 0x07 +#define CS35L41_PLL_CLK_SEL_SHIFT 0 +#define CS35L41_PLL_CLK_EN_MASK 0x10 +#define CS35L41_PLL_CLK_EN_SHIFT 4 +#define CS35L41_PLL_OPENLOOP_MASK 0x0800 +#define CS35L41_PLL_OPENLOOP_SHIFT 11 +#define CS35L41_PLLSRC_SCLK 0 +#define CS35L41_PLLSRC_LRCLK 1 +#define CS35L41_PLLSRC_SELF 3 +#define CS35L41_PLLSRC_PDMCLK 4 +#define CS35L41_PLLSRC_MCLK 5 +#define CS35L41_PLLSRC_SWIRE 7 +#define CS35L41_REFCLK_FREQ_MASK 0x7E0 +#define CS35L41_REFCLK_FREQ_SHIFT 5 + +#define CS35L41_GLOBAL_FS_MASK 0x1F +#define CS35L41_GLOBAL_FS_SHIFT 0 + +#define CS35L41_GLOBAL_EN_MASK 0x01 +#define CS35L41_GLOBAL_EN_SHIFT 0 +#define CS35L41_BST_EN_MASK 0x0030 +#define CS35L41_BST_EN_SHIFT 4 +#define CS35L41_BST_EN_DEFAULT 0x2 +#define CS35L41_AMP_EN_SHIFT 0 +#define CS35L41_AMP_EN_MASK 1 + +#define CS35L41_PDN_DONE_MASK 0x00800000 +#define CS35L41_PDN_DONE_SHIFT 23 +#define CS35L41_PUP_DONE_MASK 0x01000000 +#define CS35L41_PUP_DONE_SHIFT 24 + +#define CS35L36_PUP_DONE_IRQ_UNMASK 0x5F +#define CS35L36_PUP_DONE_IRQ_MASK 0xBF + +#define CS35L41_AMP_SHORT_ERR 0x80000000 +#define CS35L41_BST_SHORT_ERR 0x0100 +#define CS35L41_TEMP_WARN 0x8000 +#define CS35L41_TEMP_ERR 0x00020000 +#define CS35L41_BST_OVP_ERR 0x40 +#define CS35L41_BST_DCM_UVP_ERR 0x80 +#define CS35L41_OTP_BOOT_DONE 0x02 +#define CS35L41_PLL_UNLOCK 0x10 +#define CS35L41_OTP_BOOT_ERR 0x80000000 + +#define CS35L41_AMP_SHORT_ERR_RLS 0x02 +#define CS35L41_BST_SHORT_ERR_RLS 0x04 +#define CS35L41_BST_OVP_ERR_RLS 0x08 +#define CS35L41_BST_UVP_ERR_RLS 0x10 +#define CS35L41_TEMP_WARN_ERR_RLS 0x20 +#define CS35L41_TEMP_ERR_RLS 0x40 + +#define CS35L41_INT1_MASK_DEFAULT 0x7FFCFE3F +#define CS35L41_INT1_UNMASK_PUP 0xFEFFFFFF +#define CS35L41_INT1_UNMASK_PDN 0xFF7FFFFF + +#define CS35L41_GPIO_DIR_MASK 0x80000000 +#define CS35L41_GPIO_DIR_SHIFT 31 +#define CS35L41_GPIO1_CTRL_MASK 0x00030000 +#define CS35L41_GPIO1_CTRL_SHIFT 16 +#define CS35L41_GPIO2_CTRL_MASK 0x07000000 +#define CS35L41_GPIO2_CTRL_SHIFT 24 +#define CS35L41_GPIO_CTRL_OPEN_INT 2 +#define CS35L41_GPIO_CTRL_ACTV_LO 4 +#define CS35L41_GPIO_CTRL_ACTV_HI 5 +#define CS35L41_GPIO_POL_MASK 0x1000 +#define CS35L41_GPIO_POL_SHIFT 12 + +#define CS35L41_AMP_INV_PCM_SHIFT 14 +#define CS35L41_AMP_INV_PCM_MASK BIT(CS35L41_AMP_INV_PCM_SHIFT) +#define CS35L41_AMP_PCM_VOL_SHIFT 3 +#define CS35L41_AMP_PCM_VOL_MASK (0x7FF << 3) +#define CS35L41_AMP_PCM_VOL_MUTE 0x4CF + +#define CS35L41_CHIP_ID 0x35a40 +#define CS35L41R_CHIP_ID 0x35b40 +#define CS35L41_MTLREVID_MASK 0x0F +#define CS35L41_REVID_A0 0xA0 +#define CS35L41_REVID_B0 0xB0 +#define CS35L41_REVID_B2 0xB2 + +#define CS35L41_HALO_CORE_RESET 0x00000200 + +#define CS35L41_FS1_WINDOW_MASK 0x000007FF +#define CS35L41_FS2_WINDOW_MASK 0x00FFF800 +#define CS35L41_FS2_WINDOW_SHIFT 12 + +#define CS35L41_SPI_MAX_FREQ 4000000 +#define CS35L41_REGSTRIDE 4 + enum cs35l41_clk_ids { CS35L41_CLKID_SCLK = 0, CS35L41_CLKID_LRCLK = 1, @@ -31,4 +750,31 @@ struct cs35l41_platform_data { struct cs35l41_irq_cfg irq_config2; }; +struct cs35l41_otp_packed_element_t { + u32 reg; + u8 shift; + u8 size; +}; + +struct cs35l41_otp_map_element_t { + u32 id; + u32 num_elements; + const struct cs35l41_otp_packed_element_t *map; + u32 bit_offset; + u32 word_offset; +}; + +extern struct regmap_config cs35l41_regmap_i2c; +extern struct regmap_config cs35l41_regmap_spi; + +int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap); +int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap); +int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap); +int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid); +int cs35l41_set_channels(struct device *dev, struct regmap *reg, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot); +int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind, int boost_cap, + int boost_ipk); + #endif /* __CS35L41_H */ diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 96666efddb39..38ea046e653c 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -60,7 +60,6 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) * @maxburst: Maximum number of words(note: words, as in units of the * src_addr_width member, not bytes) that can be send to or received from the * DAI in one burst. - * @slave_id: Slave requester id for the DMA channel. * @filter_data: Custom DMA channel filter data, this will usually be used when * requesting the DMA channel. * @chan_name: Custom channel name to use when requesting DMA channel. @@ -74,7 +73,6 @@ struct snd_dmaengine_dai_dma_data { dma_addr_t addr; enum dma_slave_buswidth addr_width; u32 maxburst; - unsigned int slave_id; void *filter_data; const char *chan_name; unsigned int fifo_size; diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 0e45963bb767..82d9daa17851 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -8,7 +8,7 @@ #ifndef __SOUND_HDA_CODEC_H #define __SOUND_HDA_CODEC_H -#include <linux/kref.h> +#include <linux/refcount.h> #include <linux/mod_devicetable.h> #include <sound/info.h> #include <sound/control.h> @@ -166,8 +166,8 @@ struct hda_pcm { bool own_chmap; /* codec driver provides own channel maps */ /* private: */ struct hda_codec *codec; - struct kref kref; struct list_head list; + unsigned int disconnected:1; }; /* codec information */ @@ -187,6 +187,8 @@ struct hda_codec { /* PCM to create, set by patch_ops.build_pcms callback */ struct list_head pcm_list_head; + refcount_t pcm_ref; + wait_queue_head_t remove_sleep; /* codec specific info */ void *spec; @@ -420,7 +422,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm) { - kref_get(&pcm->kref); + refcount_inc(&pcm->codec->pcm_ref); } void snd_hda_codec_pcm_put(struct hda_pcm *pcm); diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 22af68b01426..6a90ce405e60 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -558,6 +558,7 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start); void snd_hdac_stream_clear(struct hdac_stream *azx_dev); void snd_hdac_stream_stop(struct hdac_stream *azx_dev); +void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus); void snd_hdac_stream_reset(struct hdac_stream *azx_dev); void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set, unsigned int streams, unsigned int reg); diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index d4e31ea16aba..77123c3e4095 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -78,36 +78,35 @@ struct hdac_ext_stream { container_of(s, struct hdac_ext_stream, hstream) void snd_hdac_ext_stream_init(struct hdac_bus *bus, - struct hdac_ext_stream *stream, int idx, - int direction, int tag); + struct hdac_ext_stream *hext_stream, int idx, + int direction, int tag); int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, - int num_stream, int dir); + int num_stream, int dir); void snd_hdac_stream_free_all(struct hdac_bus *bus); void snd_hdac_link_free_all(struct hdac_bus *bus); struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); -void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type); +void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type); void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, - struct hdac_ext_stream *azx_dev, bool decouple); + struct hdac_ext_stream *hext_stream, bool decouple); void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); -void snd_hdac_ext_stop_streams(struct hdac_bus *bus); int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value); + struct hdac_ext_stream *hext_stream, u32 value); int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, - struct hdac_ext_stream *stream); + struct hdac_ext_stream *hext_stream); void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, bool enable, int index); int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value); -int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value); + struct hdac_ext_stream *hext_stream, u32 value); +int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value); -void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hstream); -void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hstream); -void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hstream); -int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt); +void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream); +void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream); +void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream); +int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *hext_stream, int fmt); struct hdac_ext_link { struct hdac_bus *bus; diff --git a/include/sound/intel-nhlt.h b/include/sound/intel-nhlt.h index d0574805865f..089a760d36eb 100644 --- a/include/sound/intel-nhlt.h +++ b/include/sound/intel-nhlt.h @@ -10,6 +10,14 @@ #include <linux/acpi.h> +enum nhlt_link_type { + NHLT_LINK_HDA = 0, + NHLT_LINK_DSP = 1, + NHLT_LINK_DMIC = 2, + NHLT_LINK_SSP = 3, + NHLT_LINK_INVALID +}; + #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT) struct wav_fmt { @@ -33,14 +41,6 @@ struct wav_fmt_ext { u8 sub_fmt[16]; } __packed; -enum nhlt_link_type { - NHLT_LINK_HDA = 0, - NHLT_LINK_DSP = 1, - NHLT_LINK_DMIC = 2, - NHLT_LINK_SSP = 3, - NHLT_LINK_INVALID -}; - enum nhlt_device_type { NHLT_DEVICE_BT = 0, NHLT_DEVICE_DMIC = 1, @@ -132,6 +132,12 @@ void intel_nhlt_free(struct nhlt_acpi_table *addr); int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt); +bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type); +struct nhlt_specific_cfg * +intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, + u32 bus_id, u8 link_type, u8 vbps, u8 bps, + u8 num_ch, u32 rate, u8 dir, u8 dev_type); + #else struct nhlt_acpi_table; @@ -150,6 +156,21 @@ static inline int intel_nhlt_get_dmic_geo(struct device *dev, { return 0; } + +static inline bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, + u8 link_type) +{ + return false; +} + +static inline struct nhlt_specific_cfg * +intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, + u32 bus_id, u8 link_type, u8 vbps, u8 bps, + u8 num_ch, u32 rate, u8 dir, u8 dev_type) +{ + return NULL; +} + #endif #endif diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 1051b84e8579..653dfffb3ac8 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -36,13 +36,6 @@ struct snd_dma_device { #define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */ #define SNDRV_DMA_TYPE_DEV 2 /* generic device continuous */ #define SNDRV_DMA_TYPE_DEV_WC 5 /* continuous write-combined */ -#ifdef CONFIG_SND_DMA_SGBUF -#define SNDRV_DMA_TYPE_DEV_SG 3 /* generic device SG-buffer */ -#define SNDRV_DMA_TYPE_DEV_WC_SG 6 /* SG write-combined */ -#else -#define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ -#define SNDRV_DMA_TYPE_DEV_WC_SG SNDRV_DMA_TYPE_DEV_WC -#endif #ifdef CONFIG_GENERIC_ALLOCATOR #define SNDRV_DMA_TYPE_DEV_IRAM 4 /* generic device iram-buffer */ #else @@ -51,6 +44,13 @@ struct snd_dma_device { #define SNDRV_DMA_TYPE_VMALLOC 7 /* vmalloc'ed buffer */ #define SNDRV_DMA_TYPE_NONCONTIG 8 /* non-coherent SG buffer */ #define SNDRV_DMA_TYPE_NONCOHERENT 9 /* non-coherent buffer */ +#ifdef CONFIG_SND_DMA_SGBUF +#define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_NONCONTIG +#define SNDRV_DMA_TYPE_DEV_WC_SG 6 /* SG write-combined */ +#else +#define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ +#define SNDRV_DMA_TYPE_DEV_WC_SG SNDRV_DMA_TYPE_DEV_WC +#endif /* * info for buffer allocation diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 33451f8ff755..9b187d86e1bd 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -147,6 +147,9 @@ struct snd_pcm_ops { #define SNDRV_PCM_FMTBIT_S24_BE _SNDRV_PCM_FMTBIT(S24_BE) #define SNDRV_PCM_FMTBIT_U24_LE _SNDRV_PCM_FMTBIT(U24_LE) #define SNDRV_PCM_FMTBIT_U24_BE _SNDRV_PCM_FMTBIT(U24_BE) +// For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the +// available bit count in most significant bit. It's for the case of so-called 'left-justified' or +// `right-padding` sample which has less width than 32 bit. #define SNDRV_PCM_FMTBIT_S32_LE _SNDRV_PCM_FMTBIT(S32_LE) #define SNDRV_PCM_FMTBIT_S32_BE _SNDRV_PCM_FMTBIT(S32_BE) #define SNDRV_PCM_FMTBIT_U32_LE _SNDRV_PCM_FMTBIT(U32_LE) diff --git a/include/sound/rt5682s.h b/include/sound/rt5682s.h index accfbc2dcdd2..f18d91308b9a 100644 --- a/include/sound/rt5682s.h +++ b/include/sound/rt5682s.h @@ -40,6 +40,7 @@ struct rt5682s_platform_data { enum rt5682s_jd_src jd_src; unsigned int dmic_clk_rate; unsigned int dmic_delay; + unsigned int amic_delay; bool dmic_clk_driving_high; const char *dai_clk_names[RT5682S_DAI_NUM_CLKS]; diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index a4317144ab62..a52080407b98 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -148,6 +148,8 @@ struct snd_soc_component_driver { struct vm_area_struct *vma); int (*ack)(struct snd_soc_component *component, struct snd_pcm_substream *substream); + snd_pcm_sframes_t (*delay)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); const struct snd_compress_ops *compress_ops; @@ -505,5 +507,7 @@ int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd, void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd, void *stream, int rollback); int snd_soc_pcm_component_ack(struct snd_pcm_substream *substream); +void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, snd_pcm_sframes_t *codec_delay); #endif /* __SOC_COMPONENT_H */ diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 0dcb361a98bb..bbd821d2df9c 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -208,8 +208,6 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai, struct snd_pcm_substream *substream); void snd_soc_dai_shutdown(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, int rollback); -snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream); void snd_soc_dai_suspend(struct snd_soc_dai *dai); void snd_soc_dai_resume(struct snd_soc_dai *dai); int snd_soc_dai_compress_new(struct snd_soc_dai *dai, @@ -238,6 +236,8 @@ int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, int cmd, int rollback); int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream, int cmd); +void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, snd_pcm_sframes_t *codec_delay); int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, struct snd_compr_stream *cstream); @@ -295,9 +295,9 @@ struct snd_soc_dai_ops { unsigned int *rx_num, unsigned int *rx_slot); int (*set_tristate)(struct snd_soc_dai *dai, int tristate); - int (*set_sdw_stream)(struct snd_soc_dai *dai, - void *stream, int direction); - void *(*get_sdw_stream)(struct snd_soc_dai *dai, int direction); + int (*set_stream)(struct snd_soc_dai *dai, + void *stream, int direction); + void *(*get_stream)(struct snd_soc_dai *dai, int direction); /* * DAI digital mute - optional. @@ -515,42 +515,42 @@ static inline void *snd_soc_dai_get_drvdata(struct snd_soc_dai *dai) } /** - * snd_soc_dai_set_sdw_stream() - Configures a DAI for SDW stream operation + * snd_soc_dai_set_stream() - Configures a DAI for stream operation * @dai: DAI - * @stream: STREAM + * @stream: STREAM (opaque structure depending on DAI type) * @direction: Stream direction(Playback/Capture) - * SoundWire subsystem doesn't have a notion of direction and we reuse + * Some subsystems, such as SoundWire, don't have a notion of direction and we reuse * the ASoC stream direction to configure sink/source ports. * Playback maps to source ports and Capture for sink ports. * * This should be invoked with NULL to clear the stream set previously. * Returns 0 on success, a negative error code otherwise. */ -static inline int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, int direction) +static inline int snd_soc_dai_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) { - if (dai->driver->ops->set_sdw_stream) - return dai->driver->ops->set_sdw_stream(dai, stream, direction); + if (dai->driver->ops->set_stream) + return dai->driver->ops->set_stream(dai, stream, direction); else return -ENOTSUPP; } /** - * snd_soc_dai_get_sdw_stream() - Retrieves SDW stream from DAI + * snd_soc_dai_get_stream() - Retrieves stream from DAI * @dai: DAI * @direction: Stream direction(Playback/Capture) * * This routine only retrieves that was previously configured - * with snd_soc_dai_get_sdw_stream() + * with snd_soc_dai_get_stream() * * Returns pointer to stream or an ERR_PTR value, e.g. * ERR_PTR(-ENOTSUPP) if callback is not supported; */ -static inline void *snd_soc_dai_get_sdw_stream(struct snd_soc_dai *dai, - int direction) +static inline void *snd_soc_dai_get_stream(struct snd_soc_dai *dai, + int direction) { - if (dai->driver->ops->get_sdw_stream) - return dai->driver->ops->get_sdw_stream(dai, direction); + if (dai->driver->ops->get_stream) + return dai->driver->ops->get_stream(dai, direction); else return ERR_PTR(-ENOTSUPP); } diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index bc7af90099a8..75b92d883976 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -101,6 +101,8 @@ struct snd_soc_dpcm_runtime { enum snd_soc_dpcm_state state; int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */ + + int be_start; /* refcount protected by BE stream pcm lock */ }; #define for_each_dpcm_fe(be, stream, _dpcm) \ diff --git a/include/sound/soc.h b/include/sound/soc.h index 8e6dd8a257c5..7a1650b303f1 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -893,8 +893,6 @@ struct snd_soc_card { struct mutex pcm_mutex; enum snd_soc_pcm_subclass pcm_subclass; - spinlock_t dpcm_lock; - int (*probe)(struct snd_soc_card *card); int (*late_probe)(struct snd_soc_card *card); int (*remove)(struct snd_soc_card *card); @@ -1213,6 +1211,7 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname); int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, const char *propname); +int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop); int snd_soc_of_get_slot_mask(struct device_node *np, const char *prop_name, unsigned int *mask); diff --git a/include/sound/sof.h b/include/sound/sof.h index 23b374311d16..813680ab9aad 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -17,6 +17,28 @@ struct snd_sof_dsp_ops; +/** + * enum sof_fw_state - DSP firmware state definitions + * @SOF_FW_BOOT_NOT_STARTED: firmware boot is not yet started + * @SOF_FW_BOOT_PREPARE: preparing for boot (firmware loading for exaqmple) + * @SOF_FW_BOOT_IN_PROGRESS: firmware boot is in progress + * @SOF_FW_BOOT_FAILED: firmware boot failed + * @SOF_FW_BOOT_READY_FAILED: firmware booted but fw_ready op failed + * @SOF_FW_BOOT_READY_OK: firmware booted and fw_ready op passed + * @SOF_FW_BOOT_COMPLETE: firmware is booted up and functional + * @SOF_FW_CRASHED: firmware crashed after successful boot + */ +enum sof_fw_state { + SOF_FW_BOOT_NOT_STARTED = 0, + SOF_FW_BOOT_PREPARE, + SOF_FW_BOOT_IN_PROGRESS, + SOF_FW_BOOT_FAILED, + SOF_FW_BOOT_READY_FAILED, + SOF_FW_BOOT_READY_OK, + SOF_FW_BOOT_COMPLETE, + SOF_FW_CRASHED, +}; + /* * SOF Platform data. */ diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h new file mode 100644 index 000000000000..90d09dbdd709 --- /dev/null +++ b/include/sound/sof/dai-amd.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Advanced Micro Devices, Inc.. All rights reserved. + */ + +#ifndef __INCLUDE_SOUND_SOF_DAI_AMD_H__ +#define __INCLUDE_SOUND_SOF_DAI_AMD_H__ + +#include <sound/sof/header.h> + +/* ACP Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ +struct sof_ipc_dai_acp_params { + struct sof_ipc_hdr hdr; + + uint32_t fsync_rate; /* FSYNC frequency in Hz */ + uint32_t tdm_slots; +} __packed; +#endif diff --git a/include/sound/sof/dai-mediatek.h b/include/sound/sof/dai-mediatek.h new file mode 100644 index 000000000000..62dd4720558d --- /dev/null +++ b/include/sound/sof/dai-mediatek.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * Copyright(c) 2021 Mediatek Corporation. All rights reserved. + * + * Author: Bo Pan <bo.pan@mediatek.com> + */ + +#ifndef __INCLUDE_SOUND_SOF_DAI_MEDIATEK_H__ +#define __INCLUDE_SOUND_SOF_DAI_MEDIATEK_H__ + +#include <sound/sof/header.h> + +struct sof_ipc_dai_mtk_afe_params { + struct sof_ipc_hdr hdr; + u32 channels; + u32 rate; + u32 format; + u32 stream_id; + u32 reserved[4]; /* reserve for future */ +} __packed; + +#endif + diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 9625f47557b8..59ee50ac7705 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -12,6 +12,8 @@ #include <sound/sof/header.h> #include <sound/sof/dai-intel.h> #include <sound/sof/dai-imx.h> +#include <sound/sof/dai-amd.h> +#include <sound/sof/dai-mediatek.h> /* * DAI Configuration. @@ -50,12 +52,25 @@ #define SOF_DAI_FMT_INV_MASK 0x0f00 #define SOF_DAI_FMT_CLOCK_PROVIDER_MASK 0xf000 -/* DAI_CONFIG flags */ -#define SOF_DAI_CONFIG_FLAGS_MASK 0x3 -#define SOF_DAI_CONFIG_FLAGS_NONE (0 << 0) /**< DAI_CONFIG sent without stage information */ -#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS (1 << 0) /**< DAI_CONFIG sent during hw_params stage */ -#define SOF_DAI_CONFIG_FLAGS_HW_FREE (2 << 0) /**< DAI_CONFIG sent during hw_free stage */ -#define SOF_DAI_CONFIG_FLAGS_RFU (3 << 0) /**< not used, reserved for future use */ +/* + * DAI_CONFIG flags. The 4 LSB bits are used for the commands, HW_PARAMS, HW_FREE and PAUSE + * representing when the IPC is sent. The 4 MSB bits are used to add quirks along with the above + * commands. + */ +#define SOF_DAI_CONFIG_FLAGS_CMD_MASK 0xF +#define SOF_DAI_CONFIG_FLAGS_NONE 0 /**< DAI_CONFIG sent without stage information */ +#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS BIT(0) /**< DAI_CONFIG sent during hw_params stage */ +#define SOF_DAI_CONFIG_FLAGS_HW_FREE BIT(1) /**< DAI_CONFIG sent during hw_free stage */ +/**< DAI_CONFIG sent during pause trigger. Only available ABI 3.20 onwards */ +#define SOF_DAI_CONFIG_FLAGS_PAUSE BIT(2) +#define SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT 4 +#define SOF_DAI_CONFIG_FLAGS_QUIRK_MASK (0xF << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT) +/* + * This should be used along with the SOF_DAI_CONFIG_FLAGS_HW_PARAMS to indicate that pipeline + * stop/pause and DAI DMA stop/pause should happen in two steps. This change is only available + * ABI 3.20 onwards. + */ +#define SOF_DAI_CONFIG_FLAGS_2_STEP_STOP BIT(0) /** \brief Types of DAI */ enum sof_ipc_dai_type { @@ -66,6 +81,10 @@ enum sof_ipc_dai_type { SOF_DAI_INTEL_ALH, /**< Intel ALH */ SOF_DAI_IMX_SAI, /**< i.MX SAI */ SOF_DAI_IMX_ESAI, /**< i.MX ESAI */ + SOF_DAI_AMD_BT, /**< AMD ACP BT*/ + SOF_DAI_AMD_SP, /**< AMD ACP SP */ + SOF_DAI_AMD_DMIC, /**< AMD ACP DMIC */ + SOF_DAI_MEDIATEK_AFE, /**< Mediatek AFE */ }; /* general purpose DAI configuration */ @@ -90,6 +109,10 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_alh_params alh; struct sof_ipc_dai_esai_params esai; struct sof_ipc_dai_sai_params sai; + struct sof_ipc_dai_acp_params acpbt; + struct sof_ipc_dai_acp_params acpsp; + struct sof_ipc_dai_acp_params acpdmic; + struct sof_ipc_dai_mtk_afe_params afe; }; } __packed; diff --git a/include/sound/sof/debug.h b/include/sound/sof/debug.h index 3ecb5793789d..38693e3fb514 100644 --- a/include/sound/sof/debug.h +++ b/include/sound/sof/debug.h @@ -19,6 +19,8 @@ enum sof_ipc_dbg_mem_zone { SOF_IPC_MEM_ZONE_SYS_RUNTIME = 1, /**< System-runtime zone */ SOF_IPC_MEM_ZONE_RUNTIME = 2, /**< Runtime zone */ SOF_IPC_MEM_ZONE_BUFFER = 3, /**< Buffer zone */ + SOF_IPC_MEM_ZONE_RUNTIME_SHARED = 4, /**< System runtime zone */ + SOF_IPC_MEM_ZONE_SYS_SHARED = 5, /**< System shared zone */ }; /** ABI3.18 */ diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index 4c747c52e01b..b97a76bcb655 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -119,6 +119,7 @@ #define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002) #define SOF_IPC_TRACE_DMA_PARAMS_EXT SOF_CMD_TYPE(0x003) #define SOF_IPC_TRACE_FILTER_UPDATE SOF_CMD_TYPE(0x004) /**< ABI3.17 */ +#define SOF_IPC_TRACE_DMA_FREE SOF_CMD_TYPE(0x005) /**< ABI3.20 */ /* debug */ #define SOF_IPC_DEBUG_MEM_USAGE SOF_CMD_TYPE(0x001) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 5fbb79e30819..ef0cafe295b2 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -202,6 +202,11 @@ typedef int __bitwise snd_pcm_format_t; #define SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9) /* low three bytes */ +/* + * For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the + * available bit count in most significant bit. It's for the case of so-called 'left-justified' or + * `right-padding` sample which has less width than 32 bit. + */ #define SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10) #define SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11) #define SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12) @@ -300,7 +305,7 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ #define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ #define SNDRV_PCM_INFO_EXPLICIT_SYNC 0x10000000 /* needs explicit sync of pointers and data */ - +#define SNDRV_PCM_INFO_NO_REWINDS 0x20000000 /* hardware can only support monotonic changes of appl_ptr */ #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 02b71a8deea4..b72fa385bebf 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -140,4 +140,9 @@ #define SOF_TKN_INTEL_HDA_RATE 1500 #define SOF_TKN_INTEL_HDA_CH 1501 +/* AFE */ +#define SOF_TKN_MEDIATEK_AFE_RATE 1600 +#define SOF_TKN_MEDIATEK_AFE_CH 1601 +#define SOF_TKN_MEDIATEK_AFE_FORMAT 1602 + #endif diff --git a/sound/core/Makefile b/sound/core/Makefile index 79e1407cd0de..350d704ced98 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -19,7 +19,6 @@ snd-$(CONFIG_SND_JACK) += ctljack.o jack.o snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \ pcm_memory.o memalloc.o snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o -snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o diff --git a/sound/core/control_led.c b/sound/core/control_led.c index a95332b2b90b..207828f30983 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -509,7 +509,7 @@ static char *parse_string(char *s, char *val, size_t val_size) return s; } -static char *parse_iface(char *s, unsigned int *val) +static char *parse_iface(char *s, snd_ctl_elem_iface_t *val) { if (!strncasecmp(s, "card", 4)) *val = SNDRV_CTL_ELEM_IFACE_CARD; diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index 1ba887c7954e..ebc714b2f46b 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -32,10 +32,8 @@ int snd_oss_info_register(int dev, int num, char *string) mutex_lock(&strings); if (string == NULL) { x = snd_sndstat_strings[num][dev]; - if (x) { - kfree(x); - x = NULL; - } + kfree(x); + x = NULL; } else { x = kstrdup(string, GFP_KERNEL); if (x == NULL) { diff --git a/sound/core/jack.c b/sound/core/jack.c index 537df1e98f8a..d1e3055f2b6a 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -62,10 +62,13 @@ static int snd_jack_dev_free(struct snd_device *device) struct snd_card *card = device->card; struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl; + down_write(&card->controls_rwsem); list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) { list_del_init(&jack_kctl->list); snd_ctl_remove(card, jack_kctl->kctl); } + up_write(&card->controls_rwsem); + if (jack->private_free) jack->private_free(jack); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 9fc971a704a9..d1fcd1d5adae 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -620,6 +620,52 @@ static const struct snd_malloc_ops snd_dma_noncontig_ops = { .get_chunk_size = snd_dma_noncontig_get_chunk_size, }; +/* x86-specific SG-buffer with WC pages */ +#ifdef CONFIG_SND_DMA_SGBUF +#define sg_wc_address(it) ((unsigned long)page_address(sg_page_iter_page(it))) + +static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + void *p = snd_dma_noncontig_alloc(dmab, size); + struct sg_table *sgt = dmab->private_data; + struct sg_page_iter iter; + + if (!p) + return NULL; + for_each_sgtable_page(sgt, &iter, 0) + set_memory_wc(sg_wc_address(&iter), 1); + return p; +} + +static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab) +{ + struct sg_table *sgt = dmab->private_data; + struct sg_page_iter iter; + + for_each_sgtable_page(sgt, &iter, 0) + set_memory_wb(sg_wc_address(&iter), 1); + snd_dma_noncontig_free(dmab); +} + +static int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return dma_mmap_noncontiguous(dmab->dev.dev, area, + dmab->bytes, dmab->private_data); +} + +static const struct snd_malloc_ops snd_dma_sg_wc_ops = { + .alloc = snd_dma_sg_wc_alloc, + .free = snd_dma_sg_wc_free, + .mmap = snd_dma_sg_wc_mmap, + .sync = snd_dma_noncontig_sync, + .get_addr = snd_dma_noncontig_get_addr, + .get_page = snd_dma_noncontig_get_page, + .get_chunk_size = snd_dma_noncontig_get_chunk_size, +}; +#endif /* CONFIG_SND_DMA_SGBUF */ + /* * Non-coherent pages allocator */ @@ -679,14 +725,13 @@ static const struct snd_malloc_ops *dma_ops[] = { [SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops, [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops, [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops, +#ifdef CONFIG_SND_DMA_SGBUF + [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops, +#endif #ifdef CONFIG_GENERIC_ALLOCATOR [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops, #endif /* CONFIG_GENERIC_ALLOCATOR */ #endif /* CONFIG_HAS_DMA */ -#ifdef CONFIG_SND_DMA_SGBUF - [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops, - [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops, -#endif }; static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 20a0a4771b9a..3ee9edf85815 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2065,7 +2065,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr int err, cmd; #ifdef OSS_DEBUG - pcm_dbg(substream->pcm, "pcm_oss: trigger = 0x%x\n", trigger); + pr_debug("pcm_oss: trigger = 0x%x\n", trigger); #endif psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6fd3677685d7..ba4a987ed1c6 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -810,7 +810,11 @@ EXPORT_SYMBOL(snd_pcm_new_internal); static void free_chmap(struct snd_pcm_str *pstr) { if (pstr->chmap_kctl) { - snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); + struct snd_card *card = pstr->pcm->card; + + down_write(&card->controls_rwsem); + snd_ctl_remove(card, pstr->chmap_kctl); + up_write(&card->controls_rwsem); pstr->chmap_kctl = NULL; } } diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 1fc2fa077574..af6f717e1e7e 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -91,8 +91,8 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); * @dma_data: DAI DMA data * @slave_config: DMA slave configuration * - * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and - * slave_id fields of the DMA slave config from the same fields of the DAI DMA + * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width + * fields of the DMA slave config from the same fields of the DAI DMA * data struct. The src and dst fields will be initialized depending on the * direction of the substream. If the substream is a playback stream the dst * fields will be initialized, if it is a capture stream the src fields will be @@ -124,7 +124,6 @@ void snd_dmaengine_pcm_set_config_from_dai_data( slave_config->src_addr_width = dma_data->addr_width; } - slave_config->slave_id = dma_data->slave_id; slave_config->peripheral_config = dma_data->peripheral_config; slave_config->peripheral_size = dma_data->peripheral_size; } diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 4f4b4739f987..f2090025236b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2128,11 +2128,28 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff; int ret; if (old_appl_ptr == appl_ptr) return 0; + if (appl_ptr >= runtime->boundary) + return -EINVAL; + /* + * check if a rewind is requested by the application + */ + if (substream->runtime->info & SNDRV_PCM_INFO_NO_REWINDS) { + diff = appl_ptr - old_appl_ptr; + if (diff >= 0) { + if (diff > runtime->buffer_size) + return -EINVAL; + } else { + if (runtime->boundary + diff > runtime->buffer_size) + return -EINVAL; + } + } + runtime->control->appl_ptr = appl_ptr; if (substream->ops->ack) { ret = substream->ops->ack(substream); diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index d6c02dea976c..bc933104c3ee 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -235,12 +235,15 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name) /* -------------------------------------------------------- */ +#define MAX_CELL_PROCESSES_IN_QUEUE 1000 + void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) { unsigned long flags; struct snd_seq_event_cell *cell; snd_seq_tick_time_t cur_tick; snd_seq_real_time_t cur_time; + int processed = 0; if (q == NULL) return; @@ -263,6 +266,8 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) if (!cell) break; snd_seq_dispatch_event(cell, atomic, hop); + if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE) + goto out; /* the rest processed at the next batch */ } /* Process time queue... */ @@ -272,14 +277,19 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) if (!cell) break; snd_seq_dispatch_event(cell, atomic, hop); + if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE) + goto out; /* the rest processed at the next batch */ } + out: /* free lock */ spin_lock_irqsave(&q->check_lock, flags); if (q->check_again) { q->check_again = 0; - spin_unlock_irqrestore(&q->check_lock, flags); - goto __again; + if (processed < MAX_CELL_PROCESSES_IN_QUEUE) { + spin_unlock_irqrestore(&q->check_lock, flags); + goto __again; + } } q->check_blocked = 0; spin_unlock_irqrestore(&q->check_lock, flags); diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 4abc38c70cae..f5cae49500c8 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -263,6 +263,16 @@ static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream) } /* + * drain output work queue + */ +static void snd_virmidi_output_drain(struct snd_rawmidi_substream *substream) +{ + struct snd_virmidi *vmidi = substream->runtime->private_data; + + flush_work(&vmidi->output_work); +} + +/* * subscribe callback - allow output to rawmidi device */ static int snd_virmidi_subscribe(void *private_data, @@ -336,6 +346,7 @@ static const struct snd_rawmidi_ops snd_virmidi_output_ops = { .open = snd_virmidi_output_open, .close = snd_virmidi_output_close, .trigger = snd_virmidi_output_trigger, + .drain = snd_virmidi_output_drain, }; /* diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c deleted file mode 100644 index 8352a5cdb19f..000000000000 --- a/sound/core/sgbuf.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Scatter-Gather buffer - * - * Copyright (c) by Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/vmalloc.h> -#include <linux/export.h> -#include <sound/memalloc.h> -#include "memalloc_local.h" - -struct snd_sg_page { - void *buf; - dma_addr_t addr; -}; - -struct snd_sg_buf { - int size; /* allocated byte size */ - int pages; /* allocated pages */ - int tblsize; /* allocated table size */ - struct snd_sg_page *table; /* address table */ - struct page **page_table; /* page table (for vmap/vunmap) */ - struct device *dev; -}; - -/* table entries are align to 32 */ -#define SGBUF_TBL_ALIGN 32 -#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN) - -static void snd_dma_sg_free(struct snd_dma_buffer *dmab) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - struct snd_dma_buffer tmpb; - int i; - - if (!sgbuf) - return; - - vunmap(dmab->area); - dmab->area = NULL; - - tmpb.dev.type = SNDRV_DMA_TYPE_DEV; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) - tmpb.dev.type = SNDRV_DMA_TYPE_DEV_WC; - tmpb.dev.dev = sgbuf->dev; - for (i = 0; i < sgbuf->pages; i++) { - if (!(sgbuf->table[i].addr & ~PAGE_MASK)) - continue; /* continuous pages */ - tmpb.area = sgbuf->table[i].buf; - tmpb.addr = sgbuf->table[i].addr & PAGE_MASK; - tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT; - snd_dma_free_pages(&tmpb); - } - - kfree(sgbuf->table); - kfree(sgbuf->page_table); - kfree(sgbuf); - dmab->private_data = NULL; -} - -#define MAX_ALLOC_PAGES 32 - -static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size) -{ - struct snd_sg_buf *sgbuf; - unsigned int i, pages, chunk, maxpages; - struct snd_dma_buffer tmpb; - struct snd_sg_page *table; - struct page **pgtable; - int type = SNDRV_DMA_TYPE_DEV; - pgprot_t prot = PAGE_KERNEL; - void *area; - - dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL); - if (!sgbuf) - return NULL; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) { - type = SNDRV_DMA_TYPE_DEV_WC; -#ifdef pgprot_noncached - prot = pgprot_noncached(PAGE_KERNEL); -#endif - } - sgbuf->dev = dmab->dev.dev; - pages = snd_sgbuf_aligned_pages(size); - sgbuf->tblsize = sgbuf_align_table(pages); - table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL); - if (!table) - goto _failed; - sgbuf->table = table; - pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL); - if (!pgtable) - goto _failed; - sgbuf->page_table = pgtable; - - /* allocate pages */ - maxpages = MAX_ALLOC_PAGES; - while (pages > 0) { - chunk = pages; - /* don't be too eager to take a huge chunk */ - if (chunk > maxpages) - chunk = maxpages; - chunk <<= PAGE_SHIFT; - if (snd_dma_alloc_pages_fallback(type, dmab->dev.dev, - chunk, &tmpb) < 0) { - if (!sgbuf->pages) - goto _failed; - size = sgbuf->pages * PAGE_SIZE; - break; - } - chunk = tmpb.bytes >> PAGE_SHIFT; - for (i = 0; i < chunk; i++) { - table->buf = tmpb.area; - table->addr = tmpb.addr; - if (!i) - table->addr |= chunk; /* mark head */ - table++; - *pgtable++ = virt_to_page(tmpb.area); - tmpb.area += PAGE_SIZE; - tmpb.addr += PAGE_SIZE; - } - sgbuf->pages += chunk; - pages -= chunk; - if (chunk < maxpages) - maxpages = chunk; - } - - sgbuf->size = size; - area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot); - if (!area) - goto _failed; - return area; - - _failed: - snd_dma_sg_free(dmab); /* free the table */ - return NULL; -} - -static dma_addr_t snd_dma_sg_get_addr(struct snd_dma_buffer *dmab, - size_t offset) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - dma_addr_t addr; - - addr = sgbuf->table[offset >> PAGE_SHIFT].addr; - addr &= ~((dma_addr_t)PAGE_SIZE - 1); - return addr + offset % PAGE_SIZE; -} - -static struct page *snd_dma_sg_get_page(struct snd_dma_buffer *dmab, - size_t offset) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - unsigned int idx = offset >> PAGE_SHIFT; - - if (idx >= (unsigned int)sgbuf->pages) - return NULL; - return sgbuf->page_table[idx]; -} - -static unsigned int snd_dma_sg_get_chunk_size(struct snd_dma_buffer *dmab, - unsigned int ofs, - unsigned int size) -{ - struct snd_sg_buf *sg = dmab->private_data; - unsigned int start, end, pg; - - start = ofs >> PAGE_SHIFT; - end = (ofs + size - 1) >> PAGE_SHIFT; - /* check page continuity */ - pg = sg->table[start].addr >> PAGE_SHIFT; - for (;;) { - start++; - if (start > end) - break; - pg++; - if ((sg->table[start].addr >> PAGE_SHIFT) != pg) - return (start << PAGE_SHIFT) - ofs; - } - /* ok, all on continuous pages */ - return size; -} - -static int snd_dma_sg_mmap(struct snd_dma_buffer *dmab, - struct vm_area_struct *area) -{ - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) - area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); - return -ENOENT; /* continue with the default mmap handler */ -} - -const struct snd_malloc_ops snd_dma_sg_ops = { - .alloc = snd_dma_sg_alloc, - .free = snd_dma_sg_free, - .get_addr = snd_dma_sg_get_addr, - .get_page = snd_dma_sg_get_page, - .get_chunk_size = snd_dma_sg_get_chunk_size, - .mmap = snd_dma_sg_mmap, -}; diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index 7f7eed6faaae..58012de90c38 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -90,15 +90,12 @@ static int snd_virmidi_probe(struct platform_device *devptr) } for (idx = 0; idx < midi_devs[dev]; idx++) { struct snd_rawmidi *rmidi; - struct snd_virmidi_dev *rdev; err = snd_virmidi_new(card, idx, &rmidi); if (err < 0) return err; - rdev = rmidi->private_data; vmidi->midi[idx] = rmidi; strcpy(rmidi->name, "Virtual Raw MIDI"); - rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; } strcpy(card->driver, "VirMIDI"); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 37154ed43bd5..d2b5724b463f 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -18,7 +18,7 @@ /** * snd_hdac_ext_stream_init - initialize each stream (aka device) * @bus: HD-audio core bus - * @stream: HD-audio ext core stream object to initialize + * @hext_stream: HD-audio ext core stream object to initialize * @idx: stream index number * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) * @tag: the tag id to assign @@ -27,34 +27,34 @@ * invoke hdac stream initialization routine */ void snd_hdac_ext_stream_init(struct hdac_bus *bus, - struct hdac_ext_stream *stream, - int idx, int direction, int tag) + struct hdac_ext_stream *hext_stream, + int idx, int direction, int tag) { if (bus->ppcap) { - stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + + hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + AZX_PPHC_INTERVAL * idx; - stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + + hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + AZX_PPLC_MULTI * bus->num_streams + AZX_PPLC_INTERVAL * idx; } if (bus->spbcap) { - stream->spib_addr = bus->spbcap + AZX_SPB_BASE + + hext_stream->spib_addr = bus->spbcap + AZX_SPB_BASE + AZX_SPB_INTERVAL * idx + AZX_SPB_SPIB; - stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + + hext_stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + AZX_SPB_INTERVAL * idx + AZX_SPB_MAXFIFO; } if (bus->drsmcap) - stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + + hext_stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + AZX_DRSM_INTERVAL * idx; - stream->decoupled = false; - snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag); + hext_stream->decoupled = false; + snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); @@ -67,18 +67,18 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); * @dir: direction of streams */ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, - int num_stream, int dir) + int num_stream, int dir) { int stream_tag = 0; int i, tag, idx = start_idx; for (i = 0; i < num_stream; i++) { - struct hdac_ext_stream *stream = - kzalloc(sizeof(*stream), GFP_KERNEL); - if (!stream) + struct hdac_ext_stream *hext_stream = + kzalloc(sizeof(*hext_stream), GFP_KERNEL); + if (!hext_stream) return -ENOMEM; tag = ++stream_tag; - snd_hdac_ext_stream_init(bus, stream, idx, dir, tag); + snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag); idx++; } @@ -95,22 +95,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); void snd_hdac_stream_free_all(struct hdac_bus *bus) { struct hdac_stream *s, *_s; - struct hdac_ext_stream *stream; + struct hdac_ext_stream *hext_stream; list_for_each_entry_safe(s, _s, &bus->stream_list, list) { - stream = stream_to_hdac_ext_stream(s); - snd_hdac_ext_stream_decouple(bus, stream, false); + hext_stream = stream_to_hdac_ext_stream(s); + snd_hdac_ext_stream_decouple(bus, hext_stream, false); list_del(&s->list); - kfree(stream); + kfree(hext_stream); } } EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, - struct hdac_ext_stream *stream, + struct hdac_ext_stream *hext_stream, bool decouple) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; u32 val; int mask = AZX_PPCTL_PROCEN(hstream->index); @@ -121,76 +121,76 @@ void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, else if (!decouple && val) snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0); - stream->decoupled = decouple; + hext_stream->decoupled = decouple; } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked); /** * snd_hdac_ext_stream_decouple - decouple the hdac stream * @bus: HD-audio core bus - * @stream: HD-audio ext core stream object to initialize + * @hext_stream: HD-audio ext core stream object to initialize * @decouple: flag to decouple */ void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, - struct hdac_ext_stream *stream, bool decouple) + struct hdac_ext_stream *hext_stream, bool decouple) { spin_lock_irq(&bus->reg_lock); - snd_hdac_ext_stream_decouple_locked(bus, stream, decouple); + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple); spin_unlock_irq(&bus->reg_lock); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); /** * snd_hdac_ext_link_stream_start - start a stream - * @stream: HD-audio ext core stream to start + * @hext_stream: HD-audio ext core stream to start */ -void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream) +void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream) { - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN); } EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start); /** * snd_hdac_ext_link_stream_clear - stop a stream DMA - * @stream: HD-audio ext core stream to stop + * @hext_stream: HD-audio ext core stream to stop */ -void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream) +void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream) { - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear); /** * snd_hdac_ext_link_stream_reset - reset a stream - * @stream: HD-audio ext core stream to reset + * @hext_stream: HD-audio ext core stream to reset */ -void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream) +void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream) { unsigned char val; int timeout; - snd_hdac_ext_link_stream_clear(stream); + snd_hdac_ext_link_stream_clear(hext_stream); - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST); udelay(3); timeout = 50; do { - val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & + val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; if (val) break; udelay(3); } while (--timeout); val &= ~AZX_PPLCCTL_STRST; - writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); + writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); udelay(3); timeout = 50; /* waiting for hardware to report that the stream is out of reset */ do { - val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; + val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; if (!val) break; udelay(3); @@ -201,24 +201,24 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset); /** * snd_hdac_ext_link_stream_setup - set up the SD for streaming - * @stream: HD-audio ext core stream to set up + * @hext_stream: HD-audio ext core stream to set up * @fmt: stream format */ -int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt) +int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *hext_stream, int fmt) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; unsigned int val; /* make sure the run bit is zero for SD */ - snd_hdac_ext_link_stream_clear(stream); + snd_hdac_ext_link_stream_clear(hext_stream); /* program the stream_tag */ - val = readl(stream->pplc_addr + AZX_REG_PPLCCTL); + val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL); val = (val & ~AZX_PPLCCTL_STRM_MASK) | (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT); - writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); + writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); /* program the stream format */ - writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT); + writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT); return 0; } @@ -230,7 +230,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup); * @stream: stream id */ void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, - int stream) + int stream) { snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream); } @@ -250,10 +250,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id); static struct hdac_ext_stream * hdac_ext_link_stream_assign(struct hdac_bus *bus, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; - struct hdac_stream *stream = NULL; + struct hdac_stream *hstream = NULL; if (!bus->ppcap) { dev_err(bus->dev, "stream type not supported\n"); @@ -261,22 +261,22 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus, } spin_lock_irq(&bus->reg_lock); - list_for_each_entry(stream, &bus->stream_list, list) { - struct hdac_ext_stream *hstream = container_of(stream, - struct hdac_ext_stream, - hstream); - if (stream->direction != substream->stream) + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); + if (hstream->direction != substream->stream) continue; /* check if decoupled stream and not in use is available */ - if (hstream->decoupled && !hstream->link_locked) { - res = hstream; + if (hext_stream->decoupled && !hext_stream->link_locked) { + res = hext_stream; break; } - if (!hstream->link_locked) { - snd_hdac_ext_stream_decouple_locked(bus, hstream, true); - res = hstream; + if (!hext_stream->link_locked) { + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true); + res = hext_stream; break; } } @@ -290,10 +290,10 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus, static struct hdac_ext_stream * hdac_ext_host_stream_assign(struct hdac_bus *bus, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; - struct hdac_stream *stream = NULL; + struct hdac_stream *hstream = NULL; if (!bus->ppcap) { dev_err(bus->dev, "stream type not supported\n"); @@ -301,17 +301,17 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus, } spin_lock_irq(&bus->reg_lock); - list_for_each_entry(stream, &bus->stream_list, list) { - struct hdac_ext_stream *hstream = container_of(stream, - struct hdac_ext_stream, - hstream); - if (stream->direction != substream->stream) + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); + if (hstream->direction != substream->stream) continue; - if (!stream->opened) { - if (!hstream->decoupled) - snd_hdac_ext_stream_decouple_locked(bus, hstream, true); - res = hstream; + if (!hstream->opened) { + if (!hext_stream->decoupled) + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true); + res = hext_stream; break; } } @@ -346,16 +346,17 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type) { - struct hdac_ext_stream *hstream = NULL; - struct hdac_stream *stream = NULL; + struct hdac_ext_stream *hext_stream = NULL; + struct hdac_stream *hstream = NULL; switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: - stream = snd_hdac_stream_assign(bus, substream); - if (stream) - hstream = container_of(stream, - struct hdac_ext_stream, hstream); - return hstream; + hstream = snd_hdac_stream_assign(bus, substream); + if (hstream) + hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); + return hext_stream; case HDAC_EXT_STREAM_TYPE_HOST: return hdac_ext_host_stream_assign(bus, substream); @@ -371,34 +372,34 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); /** * snd_hdac_ext_stream_release - release the assigned stream - * @stream: HD-audio ext core stream to release + * @hext_stream: HD-audio ext core stream to release * @type: type of stream (coupled, host or link stream) * * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). */ -void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) +void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type) { - struct hdac_bus *bus = stream->hstream.bus; + struct hdac_bus *bus = hext_stream->hstream.bus; switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: - snd_hdac_stream_release(&stream->hstream); + snd_hdac_stream_release(&hext_stream->hstream); break; case HDAC_EXT_STREAM_TYPE_HOST: spin_lock_irq(&bus->reg_lock); - if (stream->decoupled && !stream->link_locked) - snd_hdac_ext_stream_decouple_locked(bus, stream, false); + if (hext_stream->decoupled && !hext_stream->link_locked) + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); spin_unlock_irq(&bus->reg_lock); - snd_hdac_stream_release(&stream->hstream); + snd_hdac_stream_release(&hext_stream->hstream); break; case HDAC_EXT_STREAM_TYPE_LINK: spin_lock_irq(&bus->reg_lock); - if (stream->decoupled && !stream->hstream.opened) - snd_hdac_ext_stream_decouple_locked(bus, stream, false); - stream->link_locked = 0; - stream->link_substream = NULL; + if (hext_stream->decoupled && !hext_stream->hstream.opened) + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); + hext_stream->link_locked = 0; + hext_stream->link_substream = NULL; spin_unlock_irq(&bus->reg_lock); break; @@ -437,11 +438,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); /** * snd_hdac_ext_stream_set_spib - sets the spib value of a stream * @bus: HD-audio core bus - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * @value: spib value to set */ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value) + struct hdac_ext_stream *hext_stream, u32 value) { if (!bus->spbcap) { @@ -449,7 +450,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, return -EINVAL; } - writel(value, stream->spib_addr); + writel(value, hext_stream->spib_addr); return 0; } @@ -458,12 +459,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); /** * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream * @bus: HD-audio core bus - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * * Return maxfifo for the stream */ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, - struct hdac_ext_stream *stream) + struct hdac_ext_stream *hext_stream) { if (!bus->spbcap) { @@ -471,27 +472,10 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, return -EINVAL; } - return readl(stream->fifo_addr); + return readl(hext_stream->fifo_addr); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); - -/** - * snd_hdac_ext_stop_streams - stop all stream if running - * @bus: HD-audio core bus - */ -void snd_hdac_ext_stop_streams(struct hdac_bus *bus) -{ - struct hdac_stream *stream; - - if (bus->chip_init) { - list_for_each_entry(stream, &bus->stream_list, list) - snd_hdac_stream_stop(stream); - snd_hdac_bus_stop_chip(bus); - } -} -EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); - /** * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream * @bus: HD-audio core bus @@ -520,11 +504,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); /** * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream * @bus: HD-audio core bus - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * @value: dpib value to set */ int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value) + struct hdac_ext_stream *hext_stream, u32 value) { if (!bus->drsmcap) { @@ -532,7 +516,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, return -EINVAL; } - writel(value, stream->dpibr_addr); + writel(value, hext_stream->dpibr_addr); return 0; } @@ -540,12 +524,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); /** * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * @value: lpib value to set */ -int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) +int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value) { - snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value); + snd_hdac_stream_writel(&hext_stream->hstream, SD_LPIB, value); return 0; } diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 9867555883c3..f3582012d22f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -143,6 +143,22 @@ void snd_hdac_stream_stop(struct hdac_stream *azx_dev) EXPORT_SYMBOL_GPL(snd_hdac_stream_stop); /** + * snd_hdac_stop_streams_and_chip - stop all streams and chip if running + * @bus: HD-audio core bus + */ +void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus) +{ + struct hdac_stream *stream; + + if (bus->chip_init) { + list_for_each_entry(stream, &bus->stream_list, list) + snd_hdac_stream_stop(stream); + snd_hdac_bus_stop_chip(bus); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_stop_streams_and_chip); + +/** * snd_hdac_stream_reset - reset a stream * @azx_dev: HD-audio core stream to reset */ @@ -534,17 +550,11 @@ static void azx_timecounter_init(struct hdac_stream *azx_dev, cc->mask = CLOCKSOURCE_MASK(32); /* - * Converting from 24 MHz to ns means applying a 125/3 factor. - * To avoid any saturation issues in intermediate operations, - * the 125 factor is applied first. The division is applied - * last after reading the timecounter value. - * Applying the 1/3 factor as part of the multiplication - * requires at least 20 bits for a decent precision, however - * overflows occur after about 4 hours or less, not a option. + * Calculate the optimal mult/shift values. The counter wraps + * around after ~178.9 seconds. */ - - cc->mult = 125; /* saturation after 195 years */ - cc->shift = 0; + clocks_calc_mult_shift(&cc->mult, &cc->shift, 24000000, + NSEC_PER_SEC, 178); nsec = 0; /* audio time is elapsed time since trigger */ timecounter_init(tc, cc, nsec); diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index 4208fa8a4db5..4fb90ceb4053 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -249,13 +249,13 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .flags = FLAG_SOF, .device = 0x02c8, + .codec_hid = "ESSX8336", }, { - .flags = FLAG_SOF, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x02c8, - .codec_hid = "ESSX8336", }, /* Cometlake-H */ { @@ -278,14 +278,14 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x06c8, - }, - { .flags = FLAG_SOF, .device = 0x06c8, .codec_hid = "ESSX8336", }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x06c8, + }, #endif /* Icelake */ @@ -309,13 +309,30 @@ static const struct config_entry config_table[] = { }, #endif -/* JasperLake */ +/* Jasper Lake */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE) { .flags = FLAG_SOF, .device = 0x4dc8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF, + .device = 0x4dc8, .codec_hid = "ESSX8336", }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x4dc8, + }, #endif /* Tigerlake */ @@ -334,17 +351,17 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .flags = FLAG_SOF, .device = 0xa0c8, + .codec_hid = "ESSX8336", }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x43c8, + .device = 0xa0c8, }, { - .flags = FLAG_SOF, - .device = 0xa0c8, - .codec_hid = "ESSX8336", + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x43c8, }, #endif @@ -374,6 +391,14 @@ static const struct config_entry config_table[] = { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x51cc, }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x51cd, + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x54c8, + }, #endif }; @@ -403,7 +428,7 @@ static int snd_intel_dsp_check_dmic(struct pci_dev *pci) nhlt = intel_nhlt_init(&pci->dev); if (nhlt) { - if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) + if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_DMIC)) ret = 1; intel_nhlt_free(nhlt); } diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c index e2237239d922..128476aa7c61 100644 --- a/sound/hda/intel-nhlt.c +++ b/sound/hda/intel-nhlt.c @@ -110,3 +110,105 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt) return dmic_geo; } EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo); + +bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type) +{ + struct nhlt_endpoint *epnt; + int i; + + if (!nhlt) + return false; + + epnt = (struct nhlt_endpoint *)nhlt->desc; + for (i = 0; i < nhlt->endpoint_count; i++) { + if (epnt->linktype == link_type) + return true; + + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + return false; +} +EXPORT_SYMBOL(intel_nhlt_has_endpoint_type); + +static struct nhlt_specific_cfg * +nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch, + u32 rate, u8 vbps, u8 bps) +{ + struct nhlt_fmt_cfg *cfg = fmt->fmt_config; + struct wav_fmt *wfmt; + u16 _bps, _vbps; + int i; + + dev_dbg(dev, "Endpoint format count=%d\n", fmt->fmt_count); + + for (i = 0; i < fmt->fmt_count; i++) { + wfmt = &cfg->fmt_ext.fmt; + _bps = wfmt->bits_per_sample; + _vbps = cfg->fmt_ext.sample.valid_bits_per_sample; + + dev_dbg(dev, "Endpoint format: ch=%d fmt=%d/%d rate=%d\n", + wfmt->channels, _vbps, _bps, wfmt->samples_per_sec); + + if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate && + vbps == _vbps && bps == _bps) + return &cfg->config; + + cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size); + } + + return NULL; +} + +static bool nhlt_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, + u32 bus_id, u8 link_type, u8 dir, u8 dev_type) +{ + dev_dbg(dev, "Endpoint: vbus_id=%d link_type=%d dir=%d dev_type = %d\n", + epnt->virtual_bus_id, epnt->linktype, + epnt->direction, epnt->device_type); + + if ((epnt->virtual_bus_id != bus_id) || + (epnt->linktype != link_type) || + (epnt->direction != dir)) + return false; + + /* link of type DMIC bypasses device_type check */ + return epnt->linktype == NHLT_LINK_DMIC || + epnt->device_type == dev_type; +} + +struct nhlt_specific_cfg * +intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, + u32 bus_id, u8 link_type, u8 vbps, u8 bps, + u8 num_ch, u32 rate, u8 dir, u8 dev_type) +{ + struct nhlt_specific_cfg *cfg; + struct nhlt_endpoint *epnt; + struct nhlt_fmt *fmt; + int i; + + if (!nhlt) + return NULL; + + dev_dbg(dev, "Looking for configuration:\n"); + dev_dbg(dev, " vbus_id=%d link_type=%d dir=%d, dev_type=%d\n", + bus_id, link_type, dir, dev_type); + dev_dbg(dev, " ch=%d fmt=%d/%d rate=%d\n", num_ch, vbps, bps, rate); + dev_dbg(dev, "Endpoint count=%d\n", nhlt->endpoint_count); + + epnt = (struct nhlt_endpoint *)nhlt->desc; + + for (i = 0; i < nhlt->endpoint_count; i++) { + if (nhlt_check_ep_match(dev, epnt, bus_id, link_type, dir, dev_type)) { + fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size); + + cfg = nhlt_get_specific_cfg(dev, fmt, num_ch, rate, vbps, bps); + if (cfg) + return cfg; + } + + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + + return NULL; +} +EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob); diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index ff9480f249fe..3e56c01c4544 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -24,8 +24,9 @@ void snd_gf1_mem_lock(struct snd_gf1_mem * alloc, int xup) } } -static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc, - struct snd_gf1_mem_block * block) +static struct snd_gf1_mem_block * +snd_gf1_mem_xalloc(struct snd_gf1_mem *alloc, struct snd_gf1_mem_block *block, + const char *name) { struct snd_gf1_mem_block *pblock, *nblock; @@ -33,6 +34,12 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc, if (nblock == NULL) return NULL; *nblock = *block; + nblock->name = kstrdup(name, GFP_KERNEL); + if (!nblock->name) { + kfree(nblock); + return NULL; + } + pblock = alloc->first; while (pblock) { if (pblock->ptr > nblock->ptr) { @@ -44,7 +51,7 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc, else nblock->prev->next = nblock; mutex_unlock(&alloc->memory_mutex); - return NULL; + return nblock; } pblock = pblock->next; } @@ -198,8 +205,7 @@ struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owne if (share_id != NULL) memcpy(&block.share_id, share_id, sizeof(block.share_id)); block.owner = owner; - block.name = kstrdup(name, GFP_KERNEL); - nblock = snd_gf1_mem_xalloc(alloc, &block); + nblock = snd_gf1_mem_xalloc(alloc, &block, name); snd_gf1_mem_lock(alloc, 1); return nblock; } @@ -236,14 +242,12 @@ int snd_gf1_mem_init(struct snd_gus_card * gus) if (gus->gf1.enh_mode) { block.ptr = 0; block.size = 1024; - block.name = kstrdup("InterWave LFOs", GFP_KERNEL); - if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + if (!snd_gf1_mem_xalloc(alloc, &block, "InterWave LFOs")) return -ENOMEM; } block.ptr = gus->gf1.default_voice_address; block.size = 4; - block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL); - if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + if (!snd_gf1_mem_xalloc(alloc, &block, "Voice default (NULL's)")) return -ENOMEM; #ifdef CONFIG_SND_DEBUG snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read); diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index 491de1a623cb..5fee8e89790f 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -231,7 +231,7 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate) * If the codec doesn't support VAR, the rate must be 48000 (except * for SPDIF). * - * The valid registers are AC97_PMC_MIC_ADC_RATE, + * The valid registers are AC97_PCM_MIC_ADC_RATE, * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE. * AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted * if the codec supports them. diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index ab9d2746e804..febe1c2b7d9a 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -91,6 +91,39 @@ config SND_HDA_PATCH_LOADER start up. The "patch" file can be specified via patch module option, such as patch=hda-init. +config SND_HDA_SCODEC_CS35L41 + tristate + +config SND_HDA_SCODEC_CS35L41_I2C + tristate "Build CS35L41 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on SND_SOC + select SND_HDA_GENERIC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + help + Say Y or M here to include CS35L41 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m + +config SND_HDA_SCODEC_CS35L41_SPI + tristate "Build CS35L41 HD-audio codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on SND_SOC + select SND_HDA_GENERIC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + help + Say Y or M here to include CS35L41 SPI HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m + config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" select SND_HDA_GENERIC diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index b8fa682ce66a..3e7bc608d45f 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -27,6 +27,11 @@ snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o +# side codecs +snd-hda-scodec-cs35l41-objs := cs35l41_hda.o +snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o +snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o + # common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o @@ -45,6 +50,11 @@ obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o +# side codecs +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o + # this must be the last entry after codec drivers; # otherwise the codec patches won't be hooked before the PCI probe # when built in kernel diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c new file mode 100644 index 000000000000..30b40d865863 --- /dev/null +++ b/sound/pci/hda/cs35l41_hda.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41.c -- CS35l41 ALSA HDA audio driver +// +// Copyright 2021 Cirrus Logic, Inc. +// +// Author: Lucas Tanure <tanureal@opensource.cirrus.com> + +#include <linux/acpi.h> +#include <linux/module.h> +#include <sound/hda_codec.h> +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "hda_generic.h" +#include "hda_component.h" +#include "cs35l41_hda.h" + +static const struct reg_sequence cs35l41_hda_config[] = { + { CS35L41_PLL_CLK_CTRL, 0x00000430 }, //3200000Hz, BCLK Input, PLL_REFCLK_EN = 1 + { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, //GLOBAL_FS = 48 kHz + { CS35L41_SP_ENABLES, 0x00010000 }, //ASP_RX1_EN = 1 + { CS35L41_SP_RATE_CTRL, 0x00000021 }, //ASP_BCLK_FREQ = 3.072 MHz + { CS35L41_SP_FORMAT, 0x20200200 }, //24 bits, I2S, BCLK Slave, FSYNC Slave + { CS35L41_DAC_PCM1_SRC, 0x00000008 }, //DACPCM1_SRC = ASPRX1 + { CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, //AMP_VOL_PCM 0.0 dB + { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, //AMP_GAIN_PCM 4.5 dB + { CS35L41_PWR_CTRL2, 0x00000001 }, //AMP_EN = 1 +}; + +static const struct reg_sequence cs35l41_hda_start_bst[] = { + { CS35L41_PWR_CTRL2, 0x00000021 }, //BST_EN = 10, AMP_EN = 1 + { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1 +}; + +static const struct reg_sequence cs35l41_hda_stop_bst[] = { + { CS35L41_PWR_CTRL1, 0x00000000, 3000}, // set GLOBAL_EN = 0 +}; + +// only on amps where GPIO1 is used to control ext. VSPK switch +static const struct reg_sequence cs35l41_start_ext_vspk[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { 0x00007414, 0x08C82222 }, + { 0x0000742C, 0x00000009 }, + { 0x00011008, 0x00008001 }, + { 0x0000742C, 0x0000000F }, + { 0x0000742C, 0x00000079 }, + { 0x00007438, 0x00585941 }, + { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1 + { 0x0000742C, 0x000000F9 }, + { 0x00007438, 0x00580941 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +//only on amps where GPIO1 is used to control ext. VSPK switch +static const struct reg_sequence cs35l41_stop_ext_vspk[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { 0x00002014, 0x00000000, 3000}, //set GLOBAL_EN = 0 + { 0x0000742C, 0x00000009 }, + { 0x00007438, 0x00580941 }, + { 0x00011008, 0x00000001 }, + { 0x0000393C, 0x000000C0, 6000}, + { 0x0000393C, 0x00000000 }, + { 0x00007414, 0x00C82222 }, + { 0x0000742C, 0x00000000 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_safe_to_active[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x0000742C, 0x0000000F }, + { 0x0000742C, 0x00000079 }, + { 0x00007438, 0x00585941 }, + { CS35L41_PWR_CTRL1, 0x00000001, 2000 }, //GLOBAL_EN = 1 + { 0x0000742C, 0x000000F9 }, + { 0x00007438, 0x00580941 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_active_to_safe[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, //AMP_VOL_PCM Mute + { CS35L41_PWR_CTRL2, 0x00000000 }, //AMP_EN = 0 + { CS35L41_PWR_CTRL1, 0x00000000 }, + { 0x0000742C, 0x00000009, 2000 }, + { 0x00007438, 0x00580941 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_reset_to_safe[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { 0x00007414, 0x08C82222 }, + { 0x0000742C, 0x00000009 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_no_bst = { + .probe = cs35l41_reset_to_safe, + .num_probe = ARRAY_SIZE(cs35l41_reset_to_safe), + .open = cs35l41_hda_config, + .num_open = ARRAY_SIZE(cs35l41_hda_config), + .prepare = cs35l41_safe_to_active, + .num_prepare = ARRAY_SIZE(cs35l41_safe_to_active), + .cleanup = cs35l41_active_to_safe, + .num_cleanup = ARRAY_SIZE(cs35l41_active_to_safe), +}; + +static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_ext_bst = { + .open = cs35l41_hda_config, + .num_open = ARRAY_SIZE(cs35l41_hda_config), + .prepare = cs35l41_start_ext_vspk, + .num_prepare = ARRAY_SIZE(cs35l41_start_ext_vspk), + .cleanup = cs35l41_stop_ext_vspk, + .num_cleanup = ARRAY_SIZE(cs35l41_stop_ext_vspk), +}; + +static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_int_bst = { + .open = cs35l41_hda_config, + .num_open = ARRAY_SIZE(cs35l41_hda_config), + .prepare = cs35l41_hda_start_bst, + .num_prepare = ARRAY_SIZE(cs35l41_hda_start_bst), + .cleanup = cs35l41_hda_stop_bst, + .num_cleanup = ARRAY_SIZE(cs35l41_hda_stop_bst), +}; + +static void cs35l41_hda_playback_hook(struct device *dev, int action) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + const struct cs35l41_hda_reg_sequence *reg_seq = cs35l41->reg_seq; + struct regmap *reg = cs35l41->regmap; + int ret = 0; + + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + if (reg_seq->open) + ret = regmap_multi_reg_write(reg, reg_seq->open, reg_seq->num_open); + break; + case HDA_GEN_PCM_ACT_PREPARE: + if (reg_seq->prepare) + ret = regmap_multi_reg_write(reg, reg_seq->prepare, reg_seq->num_prepare); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + if (reg_seq->cleanup) + ret = regmap_multi_reg_write(reg, reg_seq->cleanup, reg_seq->num_cleanup); + break; + case HDA_GEN_PCM_ACT_CLOSE: + if (reg_seq->close) + ret = regmap_multi_reg_write(reg, reg_seq->close, reg_seq->num_close); + break; + } + + if (ret) + dev_warn(cs35l41->dev, "Failed to apply multi reg write: %d\n", ret); + +} + +static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + + return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num, + rx_slot); +} + +static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + struct hda_component *comps = master_data; + + if (comps && cs35l41->index >= 0 && cs35l41->index < HDA_MAX_COMPONENTS) + comps = &comps[cs35l41->index]; + else + return -EINVAL; + + if (!comps->dev) { + comps->dev = dev; + strscpy(comps->name, dev_name(dev), sizeof(comps->name)); + comps->playback_hook = cs35l41_hda_playback_hook; + comps->set_channel_map = cs35l41_hda_channel_map; + return 0; + } + + return -EBUSY; +} + +static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + struct hda_component *comps = master_data; + + if (comps[cs35l41->index].dev == dev) + memset(&comps[cs35l41->index], 0, sizeof(*comps)); +} + +static const struct component_ops cs35l41_hda_comp_ops = { + .bind = cs35l41_hda_bind, + .unbind = cs35l41_hda_unbind, +}; + +static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41, + const struct cs35l41_hda_hw_config *hw_cfg) +{ + bool internal_boost = false; + int ret; + + if (!hw_cfg) { + cs35l41->reg_seq = &cs35l41_hda_reg_seq_no_bst; + return 0; + } + + if (hw_cfg->bst_ind || hw_cfg->bst_cap || hw_cfg->bst_ipk) + internal_boost = true; + + switch (hw_cfg->gpio1_func) { + case CS35l41_VSPK_SWITCH: + regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL, + CS35L41_GPIO1_CTRL_MASK, 1 << CS35L41_GPIO1_CTRL_SHIFT); + break; + case CS35l41_SYNC: + regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL, + CS35L41_GPIO1_CTRL_MASK, 2 << CS35L41_GPIO1_CTRL_SHIFT); + break; + } + + switch (hw_cfg->gpio2_func) { + case CS35L41_INTERRUPT: + regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL, + CS35L41_GPIO2_CTRL_MASK, 2 << CS35L41_GPIO2_CTRL_SHIFT); + break; + } + + if (internal_boost) { + cs35l41->reg_seq = &cs35l41_hda_reg_seq_int_bst; + if (!(hw_cfg->bst_ind && hw_cfg->bst_cap && hw_cfg->bst_ipk)) + return -EINVAL; + ret = cs35l41_boost_config(cs35l41->dev, cs35l41->regmap, + hw_cfg->bst_ind, hw_cfg->bst_cap, hw_cfg->bst_ipk); + if (ret) + return ret; + } else { + cs35l41->reg_seq = &cs35l41_hda_reg_seq_ext_bst; + } + + ret = cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, (unsigned int *)&hw_cfg->spk_pos); + if (ret) + return ret; + + return 0; +} + +static struct cs35l41_hda_hw_config *cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, + const char *hid, int id) +{ + struct cs35l41_hda_hw_config *hw_cfg; + u32 values[HDA_MAX_COMPONENTS]; + struct acpi_device *adev; + struct device *acpi_dev; + char *property; + size_t nval; + int i, ret; + + adev = acpi_dev_get_first_match_dev(hid, NULL, -1); + if (!adev) { + dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid); + return ERR_PTR(-ENODEV); + } + + acpi_dev = get_device(acpi_get_first_physical_node(adev)); + acpi_dev_put(adev); + + property = "cirrus,dev-index"; + ret = device_property_count_u32(acpi_dev, property); + if (ret <= 0) + goto no_acpi_dsd; + + if (ret > ARRAY_SIZE(values)) { + ret = -EINVAL; + goto err; + } + nval = ret; + + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret) + goto err; + + cs35l41->index = -1; + for (i = 0; i < nval; i++) { + if (values[i] == id) { + cs35l41->index = i; + break; + } + } + if (cs35l41->index == -1) { + dev_err(cs35l41->dev, "No index found in %s\n", property); + ret = -ENODEV; + goto err; + } + + /* No devm_ version as CLSA0100, in no_acpi_dsd case, can't use devm version */ + cs35l41->reset_gpio = fwnode_gpiod_get_index(&adev->fwnode, "reset", cs35l41->index, + GPIOD_OUT_LOW, "cs35l41-reset"); + + hw_cfg = kzalloc(sizeof(*hw_cfg), GFP_KERNEL); + if (!hw_cfg) { + ret = -ENOMEM; + goto err; + } + + property = "cirrus,speaker-position"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret) + goto err_free; + hw_cfg->spk_pos = values[cs35l41->index]; + + property = "cirrus,gpio1-func"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret) + goto err_free; + hw_cfg->gpio1_func = values[cs35l41->index]; + + property = "cirrus,gpio2-func"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret) + goto err_free; + hw_cfg->gpio2_func = values[cs35l41->index]; + + property = "cirrus,boost-peak-milliamp"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret == 0) + hw_cfg->bst_ipk = values[cs35l41->index]; + + property = "cirrus,boost-ind-nanohenry"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret == 0) + hw_cfg->bst_ind = values[cs35l41->index]; + + property = "cirrus,boost-cap-microfarad"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret == 0) + hw_cfg->bst_cap = values[cs35l41->index]; + + put_device(acpi_dev); + + return hw_cfg; + +err_free: + kfree(hw_cfg); +err: + put_device(acpi_dev); + dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret); + + return ERR_PTR(ret); + +no_acpi_dsd: + /* + * Device CLSA0100 doesn't have _DSD so a gpiod_get by the label reset won't work. + * And devices created by i2c-multi-instantiate don't have their device struct pointing to + * the correct fwnode, so acpi_dev must be used here + * And devm functions expect that the device requesting the resource has the correct + * fwnode + */ + if (strncmp(hid, "CLSA0100", 8) != 0) + return ERR_PTR(-EINVAL); + + /* check I2C address to assign the index */ + cs35l41->index = id == 0x40 ? 0 : 1; + cs35l41->reset_gpio = gpiod_get_index(acpi_dev, NULL, 0, GPIOD_OUT_HIGH); + cs35l41->vspk_always_on = true; + put_device(acpi_dev); + + return NULL; +} + +int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq, + struct regmap *regmap) +{ + unsigned int int_sts, regid, reg_revid, mtl_revid, chipid, int_status; + struct cs35l41_hda_hw_config *acpi_hw_cfg; + struct cs35l41_hda *cs35l41; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + cs35l41 = devm_kzalloc(dev, sizeof(*cs35l41), GFP_KERNEL); + if (!cs35l41) + return -ENOMEM; + + cs35l41->dev = dev; + cs35l41->irq = irq; + cs35l41->regmap = regmap; + dev_set_drvdata(dev, cs35l41); + + acpi_hw_cfg = cs35l41_hda_read_acpi(cs35l41, device_name, id); + if (IS_ERR(acpi_hw_cfg)) + return PTR_ERR(acpi_hw_cfg); + + if (IS_ERR(cs35l41->reset_gpio)) { + ret = PTR_ERR(cs35l41->reset_gpio); + cs35l41->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_info(cs35l41->dev, "Reset line busy, assuming shared reset\n"); + } else { + if (ret != -EPROBE_DEFER) + dev_err(cs35l41->dev, "Failed to get reset GPIO: %d\n", ret); + goto err; + } + } + if (cs35l41->reset_gpio) { + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cs35l41->reset_gpio, 1); + } + + usleep_range(2000, 2100); + + ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, int_status, + int_status & CS35L41_OTP_BOOT_DONE, 1000, 100000); + if (ret) { + dev_err(cs35l41->dev, "Failed waiting for OTP_BOOT_DONE: %d\n", ret); + goto err; + } + + ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_sts); + if (ret || (int_sts & CS35L41_OTP_BOOT_ERR)) { + dev_err(cs35l41->dev, "OTP Boot error\n"); + ret = -EIO; + goto err; + } + + ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, ®id); + if (ret) { + dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret); + goto err; + } + + ret = regmap_read(cs35l41->regmap, CS35L41_REVID, ®_revid); + if (ret) { + dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret); + goto err; + } + + mtl_revid = reg_revid & CS35L41_MTLREVID_MASK; + + chipid = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID; + if (regid != chipid) { + dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n", regid, chipid); + ret = -ENODEV; + goto err; + } + + ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid); + if (ret) + goto err; + + ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); + if (ret) { + dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); + goto err; + } + + ret = cs35l41_hda_apply_properties(cs35l41, acpi_hw_cfg); + if (ret) + goto err; + kfree(acpi_hw_cfg); + acpi_hw_cfg = NULL; + + if (cs35l41->reg_seq->probe) { + ret = regmap_register_patch(cs35l41->regmap, cs35l41->reg_seq->probe, + cs35l41->reg_seq->num_probe); + if (ret) { + dev_err(cs35l41->dev, "Fail to apply probe reg patch: %d\n", ret); + goto err; + } + } + + ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops); + if (ret) { + dev_err(cs35l41->dev, "Register component failed: %d\n", ret); + goto err; + } + + dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid); + + return 0; + +err: + kfree(acpi_hw_cfg); + if (!cs35l41->vspk_always_on) + gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); + gpiod_put(cs35l41->reset_gpio); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_hda_probe); + +int cs35l41_hda_remove(struct device *dev) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + + component_del(cs35l41->dev, &cs35l41_hda_comp_ops); + + if (!cs35l41->vspk_always_on) + gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); + gpiod_put(cs35l41->reset_gpio); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l41_hda_remove); + + +MODULE_DESCRIPTION("CS35L41 HDA Driver"); +MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h new file mode 100644 index 000000000000..76c69a8a22f6 --- /dev/null +++ b/sound/pci/hda/cs35l41_hda.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * cs35l41_hda.h -- CS35L41 ALSA HDA audio driver + * + * Copyright 2021 Cirrus Logic, Inc. + * + * Author: Lucas Tanure <tanureal@opensource.cirrus.com> + */ + +#ifndef __CS35L41_HDA_H__ +#define __CS35L41_HDA_H__ + +#include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> +#include <linux/device.h> +#include <sound/cs35l41.h> + +enum cs35l41_hda_spk_pos { + CS35l41_LEFT, + CS35l41_RIGHT, +}; + +enum cs35l41_hda_gpio_function { + CS35L41_NOT_USED, + CS35l41_VSPK_SWITCH, + CS35L41_INTERRUPT, + CS35l41_SYNC, +}; + +struct cs35l41_hda_reg_sequence { + const struct reg_sequence *probe; + unsigned int num_probe; + const struct reg_sequence *open; + unsigned int num_open; + const struct reg_sequence *prepare; + unsigned int num_prepare; + const struct reg_sequence *cleanup; + unsigned int num_cleanup; + const struct reg_sequence *close; + unsigned int num_close; +}; + +struct cs35l41_hda_hw_config { + unsigned int spk_pos; + unsigned int gpio1_func; + unsigned int gpio2_func; + int bst_ind; + int bst_ipk; + int bst_cap; +}; + +struct cs35l41_hda { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + const struct cs35l41_hda_reg_sequence *reg_seq; + + int irq; + int index; + + /* Don't put the AMP in reset of VSPK can not be turned off */ + bool vspk_always_on; +}; + +int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq, + struct regmap *regmap); +int cs35l41_hda_remove(struct device *dev); + +#endif /*__CS35L41_HDA_H__*/ diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/pci/hda/cs35l41_hda_i2c.c new file mode 100644 index 000000000000..4a9462fb5c14 --- /dev/null +++ b/sound/pci/hda/cs35l41_hda_i2c.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41.c -- CS35l41 HDA I2C driver +// +// Copyright 2021 Cirrus Logic, Inc. +// +// Author: Lucas Tanure <tanureal@opensource.cirrus.com> + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/acpi.h> + +#include "cs35l41_hda.h" + +static int cs35l41_hda_i2c_probe(struct i2c_client *clt, const struct i2c_device_id *id) +{ + const char *device_name; + + /* Compare against the device name so it works for I2C, normal ACPI + * and for ACPI by i2c-multi-instantiate matching cases + */ + if (strstr(dev_name(&clt->dev), "CLSA0100")) + device_name = "CLSA0100"; + else if (strstr(dev_name(&clt->dev), "CSC3551")) + device_name = "CSC3551"; + else + return -ENODEV; + + return cs35l41_hda_probe(&clt->dev, device_name, clt->addr, clt->irq, + devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c)); +} + +static int cs35l41_hda_i2c_remove(struct i2c_client *clt) +{ + return cs35l41_hda_remove(&clt->dev); +} + +static const struct i2c_device_id cs35l41_hda_i2c_id[] = { + { "cs35l41-hda", 0 }, + {} +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cs35l41_acpi_hda_match[] = { + {"CLSA0100", 0 }, + {"CSC3551", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match); +#endif + +static struct i2c_driver cs35l41_i2c_driver = { + .driver = { + .name = "cs35l41-hda", + .acpi_match_table = ACPI_PTR(cs35l41_acpi_hda_match), + }, + .id_table = cs35l41_hda_i2c_id, + .probe = cs35l41_hda_i2c_probe, + .remove = cs35l41_hda_i2c_remove, +}; + +module_i2c_driver(cs35l41_i2c_driver); + +MODULE_DESCRIPTION("HDA CS35L41 driver"); +MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/pci/hda/cs35l41_hda_spi.c new file mode 100644 index 000000000000..77426e96c58f --- /dev/null +++ b/sound/pci/hda/cs35l41_hda_spi.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41.c -- CS35l41 HDA SPI driver +// +// Copyright 2021 Cirrus Logic, Inc. +// +// Author: Lucas Tanure <tanureal@opensource.cirrus.com> + +#include <linux/acpi.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include "cs35l41_hda.h" + +static int cs35l41_hda_spi_probe(struct spi_device *spi) +{ + const char *device_name; + + /* Compare against the device name so it works for SPI, normal ACPI + * and for ACPI by spi-multi-instantiate matching cases + */ + if (strstr(dev_name(&spi->dev), "CSC3551")) + device_name = "CSC3551"; + else + return -ENODEV; + + return cs35l41_hda_probe(&spi->dev, device_name, spi->chip_select, spi->irq, + devm_regmap_init_spi(spi, &cs35l41_regmap_spi)); +} + +static int cs35l41_hda_spi_remove(struct spi_device *spi) +{ + return cs35l41_hda_remove(&spi->dev); +} + +static const struct spi_device_id cs35l41_hda_spi_id[] = { + { "cs35l41-hda", 0 }, + {} +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cs35l41_acpi_hda_match[] = { + { "CSC3551", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match); +#endif + +static struct spi_driver cs35l41_spi_driver = { + .driver = { + .name = "cs35l41_hda", + .acpi_match_table = ACPI_PTR(cs35l41_acpi_hda_match), + }, + .id_table = cs35l41_hda_spi_id, + .probe = cs35l41_hda_spi_probe, + .remove = cs35l41_hda_spi_remove, +}; + +module_spi_driver(cs35l41_spi_driver); + +MODULE_DESCRIPTION("HDA CS35L41 driver"); +MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 4a854475a0e6..82c492b05667 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -92,14 +92,10 @@ static int compare_input_type(const void *ap, const void *bp) */ static void reorder_outputs(unsigned int nums, hda_nid_t *pins) { - hda_nid_t nid; - switch (nums) { case 3: case 4: - nid = pins[1]; - pins[1] = pins[2]; - pins[2] = nid; + swap(pins[1], pins[2]); break; } } diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 1c8bffc3eec6..c572fb5886d5 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -14,6 +14,7 @@ #include <sound/core.h> #include <sound/hda_codec.h> #include "hda_local.h" +#include "hda_jack.h" /* * find a matching codec id @@ -156,6 +157,12 @@ static int hda_codec_driver_remove(struct device *dev) return codec->bus->core.ext_ops->hdev_detach(&codec->core); } + refcount_dec(&codec->pcm_ref); + snd_hda_codec_disconnect_pcms(codec); + snd_hda_jack_tbl_disconnect(codec); + wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref)); + snd_power_sync_ref(codec->bus->card); + if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0c4a337c9fc0..7016b48227bf 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -703,20 +703,10 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) /* * PCM device */ -static void release_pcm(struct kref *kref) -{ - struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref); - - if (pcm->pcm) - snd_device_free(pcm->codec->card, pcm->pcm); - clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits); - kfree(pcm->name); - kfree(pcm); -} - void snd_hda_codec_pcm_put(struct hda_pcm *pcm) { - kref_put(&pcm->kref, release_pcm); + if (refcount_dec_and_test(&pcm->codec->pcm_ref)) + wake_up(&pcm->codec->remove_sleep); } EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put); @@ -731,7 +721,6 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, return NULL; pcm->codec = codec; - kref_init(&pcm->kref); va_start(args, fmt); pcm->name = kvasprintf(GFP_KERNEL, fmt, args); va_end(args); @@ -741,6 +730,7 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, } list_add_tail(&pcm->list, &codec->pcm_list_head); + refcount_inc(&codec->pcm_ref); return pcm; } EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); @@ -748,15 +738,31 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); /* * codec destructor */ +void snd_hda_codec_disconnect_pcms(struct hda_codec *codec) +{ + struct hda_pcm *pcm; + + list_for_each_entry(pcm, &codec->pcm_list_head, list) { + if (pcm->disconnected) + continue; + if (pcm->pcm) + snd_device_disconnect(codec->card, pcm->pcm); + snd_hda_codec_pcm_put(pcm); + pcm->disconnected = 1; + } +} + static void codec_release_pcms(struct hda_codec *codec) { struct hda_pcm *pcm, *n; list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) { - list_del_init(&pcm->list); + list_del(&pcm->list); if (pcm->pcm) - snd_device_disconnect(codec->card, pcm->pcm); - snd_hda_codec_pcm_put(pcm); + snd_device_free(pcm->codec->card, pcm->pcm); + clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits); + kfree(pcm->name); + kfree(pcm); } } @@ -769,6 +775,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) codec->registered = 0; } + snd_hda_codec_disconnect_pcms(codec); cancel_delayed_work_sync(&codec->jackpoll_work); if (!codec->in_freeing) snd_hda_ctls_clear(codec); @@ -792,6 +799,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) remove_conn_list(codec); snd_hdac_regmap_exit(&codec->core); codec->configured = 0; + refcount_set(&codec->pcm_ref, 1); /* reset refcount */ } EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind); @@ -958,6 +966,8 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); INIT_LIST_HEAD(&codec->conn_list); INIT_LIST_HEAD(&codec->pcm_list_head); + refcount_set(&codec->pcm_ref, 1); + init_waitqueue_head(&codec->remove_sleep); INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); codec->depop_delay = -1; @@ -1727,8 +1737,11 @@ void snd_hda_ctls_clear(struct hda_codec *codec) { int i; struct hda_nid_item *items = codec->mixers.list; + + down_write(&codec->card->controls_rwsem); for (i = 0; i < codec->mixers.used; i++) snd_ctl_remove(codec->card, items[i].kctl); + up_write(&codec->card->controls_rwsem); snd_array_free(&codec->mixers); snd_array_free(&codec->nids); } diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h new file mode 100644 index 000000000000..2e52be6db9c2 --- /dev/null +++ b/sound/pci/hda/hda_component.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * HD audio Component Binding Interface + * + * Copyright (C) 2021 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/component.h> + +#define HDA_MAX_COMPONENTS 4 +#define HDA_MAX_NAME_SIZE 50 + +struct hda_component { + struct device *dev; + char name[HDA_MAX_NAME_SIZE]; + void (*playback_hook)(struct device *dev, int action); + int (*set_channel_map)(struct device *dev, unsigned int rx_num, unsigned int *rx_slot, + unsigned int tx_num, unsigned int *tx_slot); +}; diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 930ae4002a81..75dcb14ff20a 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -504,7 +504,6 @@ static int azx_get_time_info(struct snd_pcm_substream *substream, snd_pcm_gettime(substream->runtime, system_ts); nsec = timecounter_read(&azx_dev->core.tc); - nsec = div_u64(nsec, 3); /* can be optimized */ if (audio_tstamp_config->report_delay) nsec = azx_adjust_codec_delay(substream, nsec); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index c43bd0f0338e..8e1bc8ea74fc 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -183,7 +183,7 @@ struct hda_gen_spec { struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; /* for pin sensing */ - /* current status; set in hda_geneic.c */ + /* current status; set in hda_generic.c */ unsigned int hp_jack_present:1; unsigned int line_jack_present:1; unsigned int speaker_muted:1; /* current status of speaker mute */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1b46b599a5cf..4b0338c4c543 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1350,8 +1350,12 @@ static void azx_free(struct azx *chip) if (hda->freed) return; - if (azx_has_pm_runtime(chip) && chip->running) + if (azx_has_pm_runtime(chip) && chip->running) { pm_runtime_get_noresume(&pci->dev); + pm_runtime_forbid(&pci->dev); + pm_runtime_dont_use_autosuspend(&pci->dev); + } + chip->running = 0; azx_del_card_list(chip); @@ -2489,9 +2493,14 @@ static const struct pci_device_id azx_ids[] = { /* Alderlake-P */ { PCI_DEVICE(0x8086, 0x51c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51cd), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Alderlake-M */ { PCI_DEVICE(0x8086, 0x51cc), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Alderlake-N */ + { PCI_DEVICE(0x8086, 0x54c8), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Elkhart Lake */ { PCI_DEVICE(0x8086, 0x4b55), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index f29975e3e98d..7d7786df60ea 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -158,6 +158,17 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id) return jack; } +void snd_hda_jack_tbl_disconnect(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + if (!codec->bus->shutdown && jack->jack) + snd_device_disconnect(codec->card, jack->jack); + } +} + void snd_hda_jack_tbl_clear(struct hda_codec *codec) { struct hda_jack_tbl *jack = codec->jacktbl.list; diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 2abf7aac243a..ff7d289c034b 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -69,6 +69,7 @@ struct hda_jack_tbl * snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag, int dev_id); +void snd_hda_jack_tbl_disconnect(struct hda_codec *codec); void snd_hda_jack_tbl_clear(struct hda_codec *codec); void snd_hda_jack_set_dirty_all(struct hda_codec *codec); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index d22c96eb2f8f..8621f576446b 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -137,6 +137,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, int snd_hda_codec_reset(struct hda_codec *codec); void snd_hda_codec_register(struct hda_codec *codec); void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); +void snd_hda_codec_disconnect_pcms(struct hda_codec *codec); #define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core) diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index ea700395bef4..773f4903550a 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -68,14 +68,20 @@ */ #define TEGRA194_NUM_SDO_LINES 4 +struct hda_tegra_soc { + bool has_hda2codec_2x_reset; +}; + struct hda_tegra { struct azx chip; struct device *dev; - struct reset_control *reset; + struct reset_control_bulk_data resets[3]; struct clk_bulk_data clocks[3]; + unsigned int nresets; unsigned int nclocks; void __iomem *regs; struct work_struct probe_work; + const struct hda_tegra_soc *soc; }; #ifdef CONFIG_PM @@ -170,7 +176,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev) int rc; if (!chip->running) { - rc = reset_control_assert(hda->reset); + rc = reset_control_bulk_assert(hda->nresets, hda->resets); if (rc) return rc; } @@ -187,7 +193,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev) } else { usleep_range(10, 100); - rc = reset_control_deassert(hda->reset); + rc = reset_control_bulk_deassert(hda->nresets, hda->resets); if (rc) return rc; } @@ -427,9 +433,17 @@ static int hda_tegra_create(struct snd_card *card, return 0; } +static const struct hda_tegra_soc tegra30_data = { + .has_hda2codec_2x_reset = true, +}; + +static const struct hda_tegra_soc tegra194_data = { + .has_hda2codec_2x_reset = false, +}; + static const struct of_device_id hda_tegra_match[] = { - { .compatible = "nvidia,tegra30-hda" }, - { .compatible = "nvidia,tegra194-hda" }, + { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data }, + { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data }, {}, }; MODULE_DEVICE_TABLE(of, hda_tegra_match); @@ -449,6 +463,8 @@ static int hda_tegra_probe(struct platform_device *pdev) hda->dev = &pdev->dev; chip = &hda->chip; + hda->soc = of_device_get_match_data(&pdev->dev); + err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); if (err < 0) { @@ -456,11 +472,20 @@ static int hda_tegra_probe(struct platform_device *pdev) return err; } - hda->reset = devm_reset_control_array_get_exclusive(&pdev->dev); - if (IS_ERR(hda->reset)) { - err = PTR_ERR(hda->reset); + hda->resets[hda->nresets++].id = "hda"; + hda->resets[hda->nresets++].id = "hda2hdmi"; + /* + * "hda2codec_2x" reset is not present on Tegra194. Though DT would + * be updated to reflect this, but to have backward compatibility + * below is necessary. + */ + if (hda->soc->has_hda2codec_2x_reset) + hda->resets[hda->nresets++].id = "hda2codec_2x"; + + err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets, + hda->resets); + if (err) goto out_free; - } hda->clocks[hda->nclocks++].id = "hda"; hda->clocks[hda->nclocks++].id = "hda2hdmi"; diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c index 0fb0a428428b..df0b4522babf 100644 --- a/sound/pci/hda/patch_cs8409-tables.c +++ b/sound/pci/hda/patch_cs8409-tables.c @@ -252,6 +252,7 @@ struct sub_codec cs8409_cs42l42_codec = { .init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq), .hp_jack_in = 0, .mic_jack_in = 0, + .force_status_change = 1, .paged = 1, .suspended = 1, .no_type_dect = 0, @@ -443,6 +444,7 @@ struct sub_codec dolphin_cs42l42_0 = { .init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq), .hp_jack_in = 0, .mic_jack_in = 0, + .force_status_change = 1, .paged = 1, .suspended = 1, .no_type_dect = 0, @@ -456,6 +458,7 @@ struct sub_codec dolphin_cs42l42_1 = { .init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq), .hp_jack_in = 0, .mic_jack_in = 0, + .force_status_change = 1, .paged = 1, .suspended = 1, .no_type_dect = 1, diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c index 039b9f2f8e94..aff2b5abb81e 100644 --- a/sound/pci/hda/patch_cs8409.c +++ b/sound/pci/hda/patch_cs8409.c @@ -628,15 +628,17 @@ static void cs42l42_run_jack_detect(struct sub_codec *cs42l42) cs8409_i2c_write(cs42l42, 0x1b74, 0x07); cs8409_i2c_write(cs42l42, 0x131b, 0xFD); cs8409_i2c_write(cs42l42, 0x1120, 0x80); - /* Wait ~100us*/ - usleep_range(100, 200); + /* Wait ~20ms*/ + usleep_range(20000, 25000); cs8409_i2c_write(cs42l42, 0x111f, 0x77); cs8409_i2c_write(cs42l42, 0x1120, 0xc0); } static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status) { - int status_changed = 0; + int status_changed = cs42l42->force_status_change; + + cs42l42->force_status_change = 0; /* TIP_SENSE INSERT/REMOVE */ switch (reg_ts_status) { @@ -791,6 +793,7 @@ static void cs42l42_suspend(struct sub_codec *cs42l42) cs42l42->last_page = 0; cs42l42->hp_jack_in = 0; cs42l42->mic_jack_in = 0; + cs42l42->force_status_change = 1; /* Put CS42L42 into Reset */ gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h index ade2b838590c..d0b725c7285b 100644 --- a/sound/pci/hda/patch_cs8409.h +++ b/sound/pci/hda/patch_cs8409.h @@ -305,6 +305,7 @@ struct sub_codec { unsigned int hp_jack_in:1; unsigned int mic_jack_in:1; + unsigned int force_status_change:1; unsigned int suspended:1; unsigned int paged:1; unsigned int last_page; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ffcde7409d2a..92df4f243ec6 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1535,7 +1535,7 @@ static void update_eld(struct hda_codec *codec, } } - if (!eld->eld_valid || eld->eld_size <= 0) { + if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) { eld->eld_valid = false; eld->eld_size = 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 28255e752c4a..eef973661b0a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -25,6 +25,7 @@ #include "hda_auto_parser.h" #include "hda_jack.h" #include "hda_generic.h" +#include "hda_component.h" /* keep halting ALC5505 DSP, for power saving */ #define HALT_REALTEK_ALC5505 @@ -126,6 +127,10 @@ struct alc_spec { unsigned int coef0; struct input_dev *kb_dev; u8 alc_mute_keycode_map[1]; + + /* component binding */ + struct component_match *match; + struct hda_component comps[HDA_MAX_COMPONENTS]; }; /* @@ -1924,6 +1929,7 @@ enum { ALC887_FIXUP_ASUS_BASS, ALC887_FIXUP_BASS_CHMAP, ALC1220_FIXUP_GB_DUAL_CODECS, + ALC1220_FIXUP_GB_X570, ALC1220_FIXUP_CLEVO_P950, ALC1220_FIXUP_CLEVO_PB51ED, ALC1220_FIXUP_CLEVO_PB51ED_PINS, @@ -2113,6 +2119,29 @@ static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, } } +static void alc1220_fixup_gb_x570(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + static const hda_nid_t conn1[] = { 0x0c }; + static const struct coef_fw gb_x570_coefs[] = { + WRITE_COEF(0x1a, 0x01c1), + WRITE_COEF(0x1b, 0x0202), + WRITE_COEF(0x43, 0x3005), + {} + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); + break; + case HDA_FIXUP_ACT_INIT: + alc_process_coef_fw(codec, gb_x570_coefs); + break; + } +} + static void alc1220_fixup_clevo_p950(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -2415,6 +2444,10 @@ static const struct hda_fixup alc882_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc1220_fixup_gb_dual_codecs, }, + [ALC1220_FIXUP_GB_X570] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_gb_x570, + }, [ALC1220_FIXUP_CLEVO_P950] = { .type = HDA_FIXUP_FUNC, .v.func = alc1220_fixup_clevo_p950, @@ -2517,7 +2550,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570), SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), @@ -6497,6 +6530,133 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, } } +static int comp_match_dev_name(struct device *dev, void *data) +{ + return strcmp(dev_name(dev), data) == 0; +} + +static int find_comp_by_dev_name(struct alc_spec *spec, const char *name) +{ + int i; + + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { + if (strcmp(spec->comps[i].name, name) == 0) + return i; + } + + return -ENODEV; +} + +static int comp_bind(struct device *dev) +{ + struct hda_codec *cdc = dev_to_hda_codec(dev); + struct alc_spec *spec = cdc->spec; + + return component_bind_all(dev, spec->comps); +} + +static void comp_unbind(struct device *dev) +{ + struct hda_codec *cdc = dev_to_hda_codec(dev); + struct alc_spec *spec = cdc->spec; + + component_unbind_all(dev, spec->comps); +} + +static const struct component_master_ops comp_master_ops = { + .bind = comp_bind, + .unbind = comp_unbind, +}; + +static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, + struct snd_pcm_substream *sub, int action) +{ + struct alc_spec *spec = cdc->spec; + int i; + + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { + if (spec->comps[i].dev) + spec->comps[i].playback_hook(spec->comps[i].dev, action); + } +} + +static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus, + const char *hid, int count) +{ + struct device *dev = hda_codec_dev(cdc); + struct alc_spec *spec = cdc->spec; + char *name; + int ret, i; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + for (i = 0; i < count; i++) { + name = devm_kasprintf(dev, GFP_KERNEL, + "%s-%s:00-cs35l41-hda.%d", bus, hid, i); + if (!name) + return; + component_match_add(dev, &spec->match, comp_match_dev_name, name); + } + ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); + if (ret) + codec_err(cdc, "Fail to register component aggregator %d\n", ret); + else + spec->gen.pcm_playback_hook = comp_generic_playback_hook; + break; + } +} + +static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2); +} + +static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, + struct snd_pcm_substream *sub, int action) +{ + struct alc_spec *spec = cdc->spec; + unsigned int rx_slot; + int i; + + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + rx_slot = 0; + i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.0"); + if (i >= 0) + spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot); + + rx_slot = 1; + i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.1"); + if (i >= 0) + spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot); + break; + } + + comp_generic_playback_hook(hinfo, cdc, sub, action); +} + +static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, + int action) +{ + struct device *dev = hda_codec_dev(cdc); + struct alc_spec *spec = cdc->spec; + int ret; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + component_match_add(dev, &spec->match, comp_match_dev_name, + "i2c-CLSA0100:00-cs35l41-hda.0"); + component_match_add(dev, &spec->match, comp_match_dev_name, + "i2c-CLSA0100:00-cs35l41-hda.1"); + ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); + if (ret) + codec_err(cdc, "Fail to register component aggregator %d\n", ret); + else + spec->gen.pcm_playback_hook = alc287_legion_16achg6_playback_hook; + break; + } +} + /* for alc295_fixup_hp_top_speakers */ #include "hp_x360_helper.c" @@ -6784,6 +6944,10 @@ enum { ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, ALC233_FIXUP_NO_AUDIO_JACK, ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME, + ALC285_FIXUP_LEGION_Y9000X_SPEAKERS, + ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, + ALC287_FIXUP_LEGION_16ACHG6, + ALC287_FIXUP_CS35L41_I2C_2, }; static const struct hda_fixup alc269_fixups[] = { @@ -8380,6 +8544,18 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, }, + [ALC285_FIXUP_LEGION_Y9000X_SPEAKERS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_ideapad_s740_coef, + .chained = true, + .chain_id = ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, + }, + [ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_15imhg05_speakers, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, [ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS] = { .type = HDA_FIXUP_VERBS, //.v.verbs = legion_15imhg05_coefs, @@ -8514,6 +8690,14 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, + [ALC287_FIXUP_LEGION_16ACHG6] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_16achg6_speakers, + }, + [ALC287_FIXUP_CS35L41_I2C_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -8730,6 +8914,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89c3, "HP", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), @@ -8910,6 +9095,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), @@ -8921,13 +9109,17 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), + SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940", ALC298_FIXUP_LENOVO_SPK_VOLUME), + SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), + SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP), - SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6), + SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c index fb8895af0363..a047ed0f84e9 100644 --- a/sound/pci/mixart/mixart_core.c +++ b/sound/pci/mixart/mixart_core.c @@ -23,8 +23,6 @@ #define MSG_DESCRIPTOR_SIZE 0x24 #define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4) -#define MSG_DEFAULT_SIZE 512 - #define MSG_TYPE_MASK 0x00000003 /* mask for following types */ #define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */ #define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */ @@ -444,6 +442,9 @@ irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id) struct mixart_timer_notify *notify; notify = (struct mixart_timer_notify *)mixart_msg_data; + BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data)); + if (snd_BUG_ON(notify->stream_count > ARRAY_SIZE(notify->streams))) + break; for(i=0; i<notify->stream_count; i++) { u32 buffer_id = notify->streams[i].buffer_id; diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h index fbf4731a276d..2f0e29ed5d63 100644 --- a/sound/pci/mixart/mixart_core.h +++ b/sound/pci/mixart/mixart_core.h @@ -49,6 +49,7 @@ enum mixart_message_id { MSG_CLOCK_SET_PROPERTIES = 0x200002, }; +#define MSG_DEFAULT_SIZE 512 struct mixart_msg { @@ -251,10 +252,17 @@ struct mixart_sample_pos u32 sample_pos_low_part; } __attribute__((packed)); +/* + * This structure is limited by the size of MSG_DEFAULT_SIZE. Instead of + * having MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS many streams, + * this is capped to have a total size below MSG_DEFAULT_SIZE. + */ +#define MIXART_MAX_TIMER_NOTIFY_STREAMS \ + ((MSG_DEFAULT_SIZE - sizeof(u32)) / sizeof(struct mixart_sample_pos)) struct mixart_timer_notify { u32 stream_count; - struct mixart_sample_pos streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS]; + struct mixart_sample_pos streams[MIXART_MAX_TIMER_NOTIFY_STREAMS]; } __attribute__((packed)); diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index 0f4bce1c0d4f..bf289783eafd 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -99,7 +99,7 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, return -1; switch (code) { - case SND_BELL: if (hz) hz = 1000; + case SND_BELL: if (hz) hz = 1000; break; case SND_TONE: break; default: return -1; } diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 2c6af3f8f296..7a9e45094f37 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -68,7 +68,7 @@ config SND_SOC_AMD_VANGOGH_MACH tristate "AMD Vangogh support for NAU8821 CS35L41" select SND_SOC_NAU8821 select SND_SOC_CS35L41_SPI - depends on SND_SOC_AMD_ACP5x && I2C + depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER help This option enables machine driver for Vangogh platform using NAU8821 and CS35L41 codecs. @@ -96,4 +96,11 @@ config SND_SOC_AMD_YC_MACH Say m if you have such a device. If unsure select "N". +config SND_AMD_ACP_CONFIG + tristate "AMD ACP configuration selection" + select SND_SOC_ACPI if ACPI + help + This option adds an auto detection to determine which ACP + driver modules to use + source "sound/soc/amd/acp/Kconfig" diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index f1d42bbda709..4b1f77930a4a 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -3,6 +3,7 @@ acp_audio_dma-objs := acp-pcm-dma.o snd-soc-acp-da7219mx98357-mach-objs := acp-da7219-max98357a.o snd-soc-acp-rt5645-mach-objs := acp-rt5645.o snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.o +snd-acp-config-objs := acp-config.o obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o @@ -13,3 +14,4 @@ obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/ obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/ obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/ obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/ +obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c new file mode 100644 index 000000000000..c9e1c08364f3 --- /dev/null +++ b/sound/soc/amd/acp-config.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> +// + +/* ACP machine configuration module */ + +#include <linux/acpi.h> +#include <linux/bits.h> +#include <linux/dmi.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "../sof/amd/acp.h" +#include "mach-config.h" + +static int acp_quirk_data; + +static const struct config_entry config_table[] = { + { + .flags = FLAG_AMD_SOF, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "AMD"), + DMI_MATCH(DMI_PRODUCT_NAME, "Majolica-CZN"), + }, + }, + {} + }, + }, + { + .flags = FLAG_AMD_SOF, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + }, + }, + {} + }, + }, +}; + +int snd_amd_acp_find_config(struct pci_dev *pci) +{ + const struct config_entry *table = config_table; + u16 device = pci->device; + int i; + + /* Do not enable FLAGS on older platforms with Rev id zero */ + if (!pci->revision) + return 0; + + for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) { + if (table->device != device) + continue; + if (table->dmi_table && !dmi_check_system(table->dmi_table)) + continue; + acp_quirk_data = table->flags; + return table->flags; + } + + return 0; +} +EXPORT_SYMBOL(snd_amd_acp_find_config); + +static struct snd_soc_acpi_codecs amp_rt1019 = { + .num_codecs = 1, + .codecs = {"10EC1019"} +}; + +static struct snd_soc_acpi_codecs amp_max = { + .num_codecs = 1, + .codecs = {"MX98360A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { + { + .id = "10EC5682", + .drv_name = "rt5682-rt1019", + .pdata = (void *)&acp_quirk_data, + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_rt1019, + .fw_filename = "sof-rn.ri", + .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg", + }, + { + .id = "10EC5682", + .drv_name = "rt5682-max", + .pdata = (void *)&acp_quirk_data, + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_max, + .fw_filename = "sof-rn.ri", + .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg", + }, + { + .id = "RTL5682", + .drv_name = "rt5682s-max", + .pdata = (void *)&acp_quirk_data, + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_max, + .fw_filename = "sof-rn.ri", + .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg", + }, + { + .id = "AMDI1019", + .drv_name = "renoir-dsp", + .pdata = (void *)&acp_quirk_data, + .fw_filename = "sof-rn.ri", + .sof_tplg_filename = "sof-acp.tplg", + }, + {}, +}; +EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index b2065f3fe42c..3bf86c2424ae 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -522,7 +522,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-da7219-play", .stream_name = "Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = cz_da7219_init, .dpcm_playback = 1, .stop_dma_first = 1, @@ -533,7 +533,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-da7219-cap", .stream_name = "Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_da7219_cap_ops, @@ -543,7 +543,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-max98357-play", .stream_name = "HiFi Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_playback = 1, .stop_dma_first = 1, .ops = &cz_max_play_ops, @@ -554,7 +554,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "dmic0", .stream_name = "DMIC0 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_dmic0_cap_ops, @@ -565,7 +565,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "dmic1", .stream_name = "DMIC1 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_dmic1_cap_ops, @@ -578,7 +578,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "amd-rt5682-play", .stream_name = "Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = cz_rt5682_init, .dpcm_playback = 1, .stop_dma_first = 1, @@ -589,7 +589,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "amd-rt5682-cap", .stream_name = "Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_rt5682_cap_ops, @@ -599,7 +599,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "amd-max98357-play", .stream_name = "HiFi Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_playback = 1, .stop_dma_first = 1, .ops = &cz_rt5682_max_play_ops, @@ -610,7 +610,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "dmic0", .stream_name = "DMIC0 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_rt5682_dmic0_cap_ops, @@ -621,7 +621,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "dmic1", .stream_name = "DMIC1 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_rt5682_dmic1_cap_ops, diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 1f322accd9ea..8fa2e2fde4f1 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -1003,6 +1003,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; + struct audio_drv_data *adata = dev_get_drvdata(component->dev); if (!rtd) return -EINVAL; @@ -1023,7 +1024,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, } if (bytescount > 0) { delay = do_div(bytescount, period_bytes); - runtime->delay = bytes_to_frames(runtime, delay); + adata->delay += bytes_to_frames(runtime, delay); } } else { buffersize = frames_to_bytes(runtime, runtime->buffer_size); @@ -1035,6 +1036,17 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, return bytes_to_frames(runtime, pos); } +static snd_pcm_sframes_t acp_dma_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct audio_drv_data *adata = dev_get_drvdata(component->dev); + snd_pcm_sframes_t delay = adata->delay; + + adata->delay = 0; + + return delay; +} + static int acp_dma_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -1198,6 +1210,7 @@ static const struct snd_soc_component_driver acp_asoc_platform = { .hw_params = acp_dma_hw_params, .trigger = acp_dma_trigger, .pointer = acp_dma_pointer, + .delay = acp_dma_delay, .prepare = acp_dma_prepare, .pcm_construct = acp_dma_new, }; diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c index 6d5c547a32de..a79a46646d50 100644 --- a/sound/soc/amd/acp-rt5645.c +++ b/sound/soc/amd/acp-rt5645.c @@ -111,7 +111,7 @@ static struct snd_soc_dai_link cz_dai_rt5650[] = { .name = "amd-rt5645-play", .stream_name = "RT5645_AIF1", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = cz_init, .ops = &cz_aif1_ops, SND_SOC_DAILINK_REG(designware1, codec, platform), @@ -120,7 +120,7 @@ static struct snd_soc_dai_link cz_dai_rt5650[] = { .name = "amd-rt5645-cap", .stream_name = "RT5645_AIF1", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ops = &cz_aif1_ops, SND_SOC_DAILINK_REG(designware2, codec, platform), }, diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index 85529ed7e5f5..db80a73aa593 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -151,6 +151,7 @@ struct audio_drv_data { struct snd_pcm_substream *capture_i2sbt_stream; void __iomem *acp_mmio; u32 asic_type; + snd_pcm_sframes_t delay; }; /* diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 52a1371f9e61..d5838df3064b 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -32,7 +32,7 @@ config SND_AMD_ASOC_RENOIR config SND_SOC_AMD_MACH_COMMON tristate - depends on X86 && PCI && I2C + depends on X86 && PCI && I2C && GPIOLIB select CLK_FIXED_FCH select SND_SOC_RT5682_I2C select SND_SOC_DMIC @@ -44,17 +44,15 @@ config SND_SOC_AMD_MACH_COMMON config SND_SOC_AMD_LEGACY_MACH tristate "AMD Legacy Machine Driver Support" - depends on X86 && PCI && I2C + depends on X86 && PCI && I2C && GPIOLIB select SND_SOC_AMD_MACH_COMMON - depends on X86 && PCI && I2C help This option enables legacy sound card support for ACP audio. config SND_SOC_AMD_SOF_MACH tristate "AMD SOF Machine Driver Support" - depends on X86 && PCI && I2C + depends on X86 && PCI && I2C && GPIOLIB select SND_SOC_AMD_MACH_COMMON - depends on X86 && PCI && I2C help This option enables SOF sound card support for ACP audio. diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index de0f8024e2fb..0ad1cf41b308 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -27,6 +27,7 @@ static struct acp_card_drvdata rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = NONE, + .gpio_spkr_en = EN_SPKR_GPIO_GB, }; static const struct snd_kcontrol_new acp_controls[] = { @@ -41,15 +42,16 @@ static const struct snd_kcontrol_new acp_controls[] = { static const struct snd_soc_dapm_widget acp_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", NULL), - SND_SOC_DAPM_SPK("Left Spk", NULL), - SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_SPK("Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler), }; static int acp_asoc_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; struct device *dev = &pdev->dev; + unsigned int spkr_gpio; int ret; if (!pdev->id_entry) @@ -67,9 +69,20 @@ static int acp_asoc_probe(struct platform_device *pdev) card->controls = acp_controls; card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en; acp_legacy_dai_links_create(card); + if (gpio_is_valid(spkr_gpio)) { + ret = devm_gpio_request(dev, spkr_gpio, "spkren"); + if (ret) { + dev_err(dev, "(%s) gpio request failed: %d\n", + __func__, ret); + return ret; + } + gpio_direction_output(spkr_gpio, 0); + } + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 7785f12aa006..c9caade5cb74 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -71,6 +71,31 @@ static const struct snd_soc_dapm_route rt5682_map[] = { { "IN1P", NULL, "Headset Mic" }, }; +int event_spkr_handler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct acp_card_drvdata *drvdata = snd_soc_card_get_drvdata(card); + + if (!gpio_is_valid(drvdata->gpio_spkr_en)) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + gpio_set_value(drvdata->gpio_spkr_en, 1); + break; + case SND_SOC_DAPM_PRE_PMD: + gpio_set_value(drvdata->gpio_spkr_en, 0); + break; + default: + dev_warn(card->dev, "%s invalid setting\n", __func__); + break; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(event_spkr_handler, SND_SOC_AMD_MACH); + /* Define card ops for RT5682 CODEC */ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd) { @@ -268,8 +293,8 @@ static const struct snd_soc_ops acp_card_rt5682s_ops = { /* Declare RT1019 codec components */ SND_SOC_DAILINK_DEF(rt1019, - DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:01", "rt1019-aif"), - COMP_CODEC("i2c-10EC1019:02", "rt1019-aif"))); + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"), + COMP_CODEC("i2c-10EC1019:01", "rt1019-aif"))); static const struct snd_soc_dapm_route rt1019_map_lr[] = { { "Left Spk", NULL, "Left SPO" }, @@ -278,11 +303,11 @@ static const struct snd_soc_dapm_route rt1019_map_lr[] = { static struct snd_soc_codec_conf rt1019_conf[] = { { - .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"), + .dlc = COMP_CODEC_CONF("i2c-10EC1019:00"), .name_prefix = "Left", }, { - .dlc = COMP_CODEC_CONF("i2c-10EC1019:02"), + .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"), .name_prefix = "Right", }, }; diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index 5dc47cfbff10..fd6299844ebe 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -17,6 +17,12 @@ #include <linux/input.h> #include <linux/module.h> #include <sound/soc.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> + +#define EN_SPKR_GPIO_GB 0x11F +#define EN_SPKR_GPIO_NK 0x146 +#define EN_SPKR_GPIO_NONE -EINVAL enum be_id { HEADSET_BE_ID = 0, @@ -49,9 +55,11 @@ struct acp_card_drvdata { unsigned int dai_fmt; struct clk *wclk; struct clk *bclk; + unsigned int gpio_spkr_en; }; int acp_sofdsp_dai_links_create(struct snd_soc_card *card); int acp_legacy_dai_links_create(struct snd_soc_card *card); - +int event_spkr_handler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event); #endif diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 854eb7214cea..07de46142655 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -27,6 +27,7 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, + .gpio_spkr_en = EN_SPKR_GPIO_GB, }; static struct acp_card_drvdata sof_rt5682_max_data = { @@ -36,6 +37,7 @@ static struct acp_card_drvdata sof_rt5682_max_data = { .hs_codec_id = RT5682, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, + .gpio_spkr_en = EN_SPKR_GPIO_NK, }; static struct acp_card_drvdata sof_rt5682s_max_data = { @@ -45,6 +47,7 @@ static struct acp_card_drvdata sof_rt5682s_max_data = { .hs_codec_id = RT5682S, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, + .gpio_spkr_en = EN_SPKR_GPIO_NK, }; static const struct snd_kcontrol_new acp_controls[] = { @@ -58,15 +61,16 @@ static const struct snd_kcontrol_new acp_controls[] = { static const struct snd_soc_dapm_widget acp_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", NULL), - SND_SOC_DAPM_SPK("Left Spk", NULL), - SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_SPK("Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler), }; static int acp_sof_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; struct device *dev = &pdev->dev; + unsigned int spkr_gpio; int ret; if (!pdev->id_entry) @@ -84,9 +88,20 @@ static int acp_sof_probe(struct platform_device *pdev) card->controls = acp_controls; card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en; acp_sofdsp_dai_links_create(card); + if (gpio_is_valid(spkr_gpio)) { + ret = devm_gpio_request(dev, spkr_gpio, "spkren"); + if (ret) { + dev_err(dev, "(%s) gpio request failed: %d\n", + __func__, ret); + return ret; + } + gpio_direction_output(spkr_gpio, 0); + } + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c index e561464f7d60..dad70436d063 100644 --- a/sound/soc/amd/acp3x-rt5682-max9836.c +++ b/sound/soc/amd/acp3x-rt5682-max9836.c @@ -51,7 +51,7 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd) /* set rt5682 dai fmt */ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); + | SND_SOC_DAIFMT_CBP_CFP); if (ret < 0) { dev_err(rtd->card->dev, "Failed to set rt5682 dai fmt: %d\n", ret); @@ -302,7 +302,7 @@ static struct snd_soc_dai_link acp3x_dai[] = { .name = "acp3x-5682-play", .stream_name = "Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = acp3x_5682_init, .dpcm_playback = 1, .dpcm_capture = 1, @@ -313,7 +313,7 @@ static struct snd_soc_dai_link acp3x_dai[] = { .name = "acp3x-max98357-play", .stream_name = "HiFi Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .dpcm_playback = 1, .ops = &acp3x_max_play_ops, .cpus = acp3x_bt, @@ -325,7 +325,7 @@ static struct snd_soc_dai_link acp3x_dai[] = { .name = "acp3x-ec-dmic0-capture", .stream_name = "Capture DMIC0", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .dpcm_capture = 1, .ops = &acp3x_ec_cap0_ops, SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform), diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h new file mode 100644 index 000000000000..feb3756d9ac4 --- /dev/null +++ b/sound/soc/amd/mach-config.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * + * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + */ +#ifndef __AMD_MACH_CONFIG_H +#define __AMD_MACH_CONFIG_H + +#include <sound/soc-acpi.h> + +#define FLAG_AMD_SOF BIT(1) +#define FLAG_AMD_SOF_ONLY_DMIC BIT(2) + +#define ACP_PCI_DEV_ID 0x15E2 + +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[]; + +struct config_entry { + u32 flags; + u16 device; + const struct dmi_system_id *dmi_table; +}; + +#endif diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c index e604f4ea524f..7e66393e4153 100644 --- a/sound/soc/amd/yc/acp6x-pdm-dma.c +++ b/sound/soc/amd/yc/acp6x-pdm-dma.c @@ -318,7 +318,7 @@ static int acp6x_pdm_dai_trigger(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_dai_ops acp6x_pdm_dai_ops = { +static const struct snd_soc_dai_ops acp6x_pdm_dai_ops = { .trigger = acp6x_pdm_dai_trigger, }; diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c index f9331f7e80fe..627564c18c27 100644 --- a/sound/soc/atmel/mikroe-proto.c +++ b/sound/soc/atmel/mikroe-proto.c @@ -144,9 +144,9 @@ static int snd_proto_probe(struct platform_device *pdev) of_node_put(cpu_np); ret = snd_soc_register_card(&snd_proto); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card() failed: %d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c index 1b3a31296c9b..ef537de7719c 100644 --- a/sound/soc/atmel/tse850-pcm5142.c +++ b/sound/soc/atmel/tse850-pcm5142.c @@ -371,35 +371,27 @@ static int tse850_probe(struct platform_device *pdev) } tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH); - if (IS_ERR(tse850->add)) { - if (PTR_ERR(tse850->add) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'add' gpio\n"); - return PTR_ERR(tse850->add); - } + if (IS_ERR(tse850->add)) + return dev_err_probe(dev, PTR_ERR(tse850->add), + "failed to get 'add' gpio\n"); tse850->add_cache = 1; tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH); - if (IS_ERR(tse850->loop1)) { - if (PTR_ERR(tse850->loop1) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'loop1' gpio\n"); - return PTR_ERR(tse850->loop1); - } + if (IS_ERR(tse850->loop1)) + return dev_err_probe(dev, PTR_ERR(tse850->loop1), + "failed to get 'loop1' gpio\n"); tse850->loop1_cache = 1; tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH); - if (IS_ERR(tse850->loop2)) { - if (PTR_ERR(tse850->loop2) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'loop2' gpio\n"); - return PTR_ERR(tse850->loop2); - } + if (IS_ERR(tse850->loop2)) + return dev_err_probe(dev, PTR_ERR(tse850->loop2), + "failed to get 'loop2' gpio\n"); tse850->loop2_cache = 1; tse850->ana = devm_regulator_get(dev, "axentia,ana"); - if (IS_ERR(tse850->ana)) { - if (PTR_ERR(tse850->ana) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'ana' regulator\n"); - return PTR_ERR(tse850->ana); - } + if (IS_ERR(tse850->ana)) + return dev_err_probe(dev, PTR_ERR(tse850->ana), + "failed to get 'ana' regulator\n"); ret = regulator_enable(tse850->ana); if (ret < 0) { diff --git a/sound/soc/bcm/bcm63xx-i2s.h b/sound/soc/bcm/bcm63xx-i2s.h index edc328ba53d3..f30556bec89e 100644 --- a/sound/soc/bcm/bcm63xx-i2s.h +++ b/sound/soc/bcm/bcm63xx-i2s.h @@ -74,7 +74,6 @@ struct bcm_i2s_priv { struct device *dev; - struct resource *r_irq; struct regmap *regmap_i2s; struct clk *i2s_clk; struct snd_pcm_substream *play_substream; diff --git a/sound/soc/bcm/bcm63xx-pcm-whistler.c b/sound/soc/bcm/bcm63xx-pcm-whistler.c index b5096f64c576..2c600b017524 100644 --- a/sound/soc/bcm/bcm63xx-pcm-whistler.c +++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c @@ -6,6 +6,7 @@ #include <linux/dma-mapping.h> #include <linux/io.h> +#include <linux/irq.h> #include <linux/module.h> #include <sound/pcm_params.h> #include <linux/regmap.h> @@ -387,14 +388,12 @@ int bcm63xx_soc_platform_probe(struct platform_device *pdev, { int ret; - i2s_priv->r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!i2s_priv->r_irq) { - dev_err(&pdev->dev, "Unable to get register irq resource.\n"); - return -ENODEV; - } + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; - ret = devm_request_irq(&pdev->dev, i2s_priv->r_irq->start, i2s_dma_isr, - i2s_priv->r_irq->flags, "i2s_dma", (void *)i2s_priv); + ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr, + irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv); if (ret) { dev_err(&pdev->dev, "i2s_init: failed to request interrupt.ret=%d\n", ret); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 326f2d611ad4..d3e5ae8310ef 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -41,6 +41,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ADS117X imply SND_SOC_AK4104 imply SND_SOC_AK4118 + imply SND_SOC_AK4375 imply SND_SOC_AK4458 imply SND_SOC_AK4535 imply SND_SOC_AK4554 @@ -220,6 +221,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TDA7419 imply SND_SOC_TFA9879 imply SND_SOC_TFA989X + imply SND_SOC_TLV320ADC3XXX imply SND_SOC_TLV320ADCX140 imply SND_SOC_TLV320AIC23_I2C imply SND_SOC_TLV320AIC23_SPI @@ -241,8 +243,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_UDA1380 imply SND_SOC_WCD9335 imply SND_SOC_WCD934X - imply SND_SOC_WCD937X - imply SND_SOC_WCD938X + imply SND_SOC_WCD938X_SDW imply SND_SOC_LPASS_RX_MACRO imply SND_SOC_LPASS_TX_MACRO imply SND_SOC_WL1273 @@ -343,11 +344,15 @@ config SND_SOC_WM_ADSP 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 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 config SND_SOC_AB8500_CODEC tristate @@ -519,6 +524,16 @@ config SND_SOC_AK4118 depends on I2C select REGMAP_I2C +config SND_SOC_AK4375 + tristate "AKM AK4375 CODEC" + depends on I2C + select REGMAP_I2C + help + Enable support for the Asahi-Kasei AK4375 codec. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-ak4375. + config SND_SOC_AK4458 tristate "AKM AK4458 CODEC" depends on I2C @@ -609,14 +624,24 @@ config SND_SOC_CS35L36 tristate "Cirrus Logic CS35L36 CODEC" depends on I2C +config SND_SOC_CS35L41_LIB + tristate + +config SND_SOC_CS35L41 + tristate + config SND_SOC_CS35L41_SPI tristate "Cirrus Logic CS35L41 CODEC (SPI)" depends on SPI_MASTER + select SND_SOC_CS35L41_LIB + select SND_SOC_CS35L41 select REGMAP_SPI config SND_SOC_CS35L41_I2C tristate "Cirrus Logic CS35L41 CODEC (I2C)" depends on I2C + select SND_SOC_CS35L41_LIB + select SND_SOC_CS35L41 select REGMAP_I2C config SND_SOC_CS42L42 @@ -1486,6 +1511,13 @@ config SND_SOC_TFA989X Note that the driver currently bypasses the built-in "CoolFlux DSP" and does not support (hardware) volume control. +config SND_SOC_TLV320ADC3XXX + tristate "Texas Instruments TLV320ADC3001/3101 audio ADC" + depends on I2C + help + Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 + ADCs. + config SND_SOC_TLV320AIC23 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 9acfbcbfc46d..ac7f20972470 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -34,6 +34,7 @@ snd-soc-adav803-objs := adav803.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4118-objs := ak4118.o +snd-soc-ak4375-objs := ak4375.o snd-soc-ak4458-objs := ak4458.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4554-objs := ak4554.o @@ -54,8 +55,10 @@ snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o snd-soc-cs35l35-objs := cs35l35.o snd-soc-cs35l36-objs := cs35l36.o -snd-soc-cs35l41-spi-objs := cs35l41-spi.o cs35l41.o cs35l41-tables.o -snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o cs35l41.o cs35l41-tables.o +snd-soc-cs35l41-lib-objs := cs35l41-lib.o +snd-soc-cs35l41-objs := cs35l41.o +snd-soc-cs35l41-spi-objs := cs35l41-spi.o +snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o @@ -238,6 +241,7 @@ snd-soc-tda7419-objs := tda7419.o snd-soc-tas2770-objs := tas2770.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tfa989x-objs := tfa989x.o +snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o @@ -369,6 +373,7 @@ obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4118) += snd-soc-ak4118.o +obj-$(CONFIG_SND_SOC_AK4375) += snd-soc-ak4375.o obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o @@ -391,6 +396,8 @@ obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o +obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o +obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o @@ -572,6 +579,7 @@ obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TFA989X) += snd-soc-tfa989x.o +obj-$(CONFIG_SND_SOC_TLV320ADC3XXX) += snd-soc-tlv320adc3xxx.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index c5bf461c0b7e..dba9af753188 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -13,8 +13,8 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_device.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <sound/core.h> @@ -106,8 +106,8 @@ static const char * const supply_names[] = { }; struct adau1701 { - int gpio_nreset; - int gpio_pll_mode[2]; + struct gpio_desc *gpio_nreset; + struct gpio_descs *gpio_pll_mode; unsigned int dai_fmt; unsigned int pll_clkdiv; unsigned int sysclk; @@ -303,39 +303,41 @@ static int adau1701_reset(struct snd_soc_component *component, unsigned int clkd struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); int ret; + DECLARE_BITMAP(values, 2); sigmadsp_reset(adau1701->sigmadsp); - if (clkdiv != ADAU1707_CLKDIV_UNSET && - gpio_is_valid(adau1701->gpio_pll_mode[0]) && - gpio_is_valid(adau1701->gpio_pll_mode[1])) { + if (clkdiv != ADAU1707_CLKDIV_UNSET && adau1701->gpio_pll_mode) { switch (clkdiv) { case 64: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); + __assign_bit(0, values, 0); + __assign_bit(1, values, 0); break; case 256: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); + __assign_bit(0, values, 0); + __assign_bit(1, values, 1); break; case 384: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); + __assign_bit(0, values, 1); + __assign_bit(1, values, 0); break; - case 0: /* fallback */ + case 0: /* fallback */ case 512: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); + __assign_bit(0, values, 1); + __assign_bit(1, values, 1); break; } + gpiod_set_array_value_cansleep(adau1701->gpio_pll_mode->ndescs, + adau1701->gpio_pll_mode->desc, adau1701->gpio_pll_mode->info, + values); } adau1701->pll_clkdiv = clkdiv; - if (gpio_is_valid(adau1701->gpio_nreset)) { - gpio_set_value_cansleep(adau1701->gpio_nreset, 0); + if (adau1701->gpio_nreset) { + gpiod_set_value_cansleep(adau1701->gpio_nreset, 0); /* minimum reset time is 20ns */ udelay(1); - gpio_set_value_cansleep(adau1701->gpio_nreset, 1); + gpiod_set_value_cansleep(adau1701->gpio_nreset, 1); /* power-up time may be as long as 85ms */ mdelay(85); } @@ -719,8 +721,8 @@ static void adau1701_remove(struct snd_soc_component *component) { struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(adau1701->gpio_nreset)) - gpio_set_value_cansleep(adau1701->gpio_nreset, 0); + if (adau1701->gpio_nreset) + gpiod_set_value_cansleep(adau1701->gpio_nreset, 0); regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); } @@ -788,8 +790,6 @@ static int adau1701_i2c_probe(struct i2c_client *client, { struct adau1701 *adau1701; struct device *dev = &client->dev; - int gpio_nreset = -EINVAL; - int gpio_pll_mode[2] = { -EINVAL, -EINVAL }; int ret, i; adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL); @@ -823,26 +823,6 @@ static int adau1701_i2c_probe(struct i2c_client *client, if (dev->of_node) { - gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0); - if (gpio_nreset < 0 && gpio_nreset != -ENOENT) { - ret = gpio_nreset; - goto exit_regulators_disable; - } - - gpio_pll_mode[0] = of_get_named_gpio(dev->of_node, - "adi,pll-mode-gpios", 0); - if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) { - ret = gpio_pll_mode[0]; - goto exit_regulators_disable; - } - - gpio_pll_mode[1] = of_get_named_gpio(dev->of_node, - "adi,pll-mode-gpios", 1); - if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) { - ret = gpio_pll_mode[1]; - goto exit_regulators_disable; - } - of_property_read_u32(dev->of_node, "adi,pll-clkdiv", &adau1701->pll_clkdiv); @@ -851,32 +831,20 @@ static int adau1701_i2c_probe(struct i2c_client *client, ARRAY_SIZE(adau1701->pin_config)); } - if (gpio_is_valid(gpio_nreset)) { - ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW, - "ADAU1701 Reset"); - if (ret < 0) - goto exit_regulators_disable; + adau1701->gpio_nreset = devm_gpiod_get_optional(dev, "reset", GPIOD_IN); + + if (IS_ERR(adau1701->gpio_nreset)) { + ret = PTR_ERR(adau1701->gpio_nreset); + goto exit_regulators_disable; } - if (gpio_is_valid(gpio_pll_mode[0]) && - gpio_is_valid(gpio_pll_mode[1])) { - ret = devm_gpio_request_one(dev, gpio_pll_mode[0], - GPIOF_OUT_INIT_LOW, - "ADAU1701 PLL mode 0"); - if (ret < 0) - goto exit_regulators_disable; + adau1701->gpio_pll_mode = devm_gpiod_get_array_optional(dev, "adi,pll-mode", GPIOD_OUT_LOW); - ret = devm_gpio_request_one(dev, gpio_pll_mode[1], - GPIOF_OUT_INIT_LOW, - "ADAU1701 PLL mode 1"); - if (ret < 0) - goto exit_regulators_disable; + if (IS_ERR(adau1701->gpio_pll_mode)) { + ret = PTR_ERR(adau1701->gpio_pll_mode); + goto exit_regulators_disable; } - adau1701->gpio_nreset = gpio_nreset; - adau1701->gpio_pll_mode[0] = gpio_pll_mode[0]; - adau1701->gpio_pll_mode[1] = gpio_pll_mode[1]; - i2c_set_clientdata(client, adau1701); adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index e0a6451851e8..2e6bafd2a821 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -374,20 +374,14 @@ static int ak4118_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, ak4118); ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(ak4118->reset)) { - ret = PTR_ERR(ak4118->reset); - if (ret != -EPROBE_DEFER) - dev_err(&i2c->dev, "Failed to get reset: %d\n", ret); - return ret; - } + if (IS_ERR(ak4118->reset)) + return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->reset), + "Failed to get reset\n"); ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN); - if (IS_ERR(ak4118->irq)) { - ret = PTR_ERR(ak4118->irq); - if (ret != -EPROBE_DEFER) - dev_err(&i2c->dev, "Failed to get IRQ: %d\n", ret); - return ret; - } + if (IS_ERR(ak4118->irq)) + return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->irq), + "Failed to get IRQ\n"); ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq), NULL, ak4118_irq_handler, diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c new file mode 100644 index 000000000000..9a7b662016b9 --- /dev/null +++ b/sound/soc/codecs/ak4375.c @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Based on code by Hu Jin + * Copyright (C) 2014 Asahi Kasei Microdevices Corporation + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +/* Registers and fields */ +#define AK4375_00_POWER_MANAGEMENT1 0x00 +#define PMPLL BIT(0) /* 0: PLL off, 1: PLL on */ +#define AK4375_01_POWER_MANAGEMENT2 0x01 +#define PMCP1 BIT(0) /* Charge Pump 1: LDO1 and DAC */ +#define PMCP2 BIT(1) /* Charge Pump 2: Class-G HP Amp */ +#define PMLDO1P BIT(4) +#define PMLDO1N BIT(5) +#define PMLDO (PMLDO1P | PMLDO1N) +#define AK4375_02_POWER_MANAGEMENT3 0x02 +#define AK4375_03_POWER_MANAGEMENT4 0x03 +#define AK4375_04_OUTPUT_MODE_SETTING 0x04 +#define AK4375_05_CLOCK_MODE_SELECT 0x05 +#define FS_MASK GENMASK(4, 0) +#define FS_8KHZ 0x00 +#define FS_11_025KHZ 0x01 +#define FS_16KHZ 0x04 +#define FS_22_05KHZ 0x05 +#define FS_32KHZ 0x08 +#define FS_44_1KHZ 0x09 +#define FS_48KHZ 0x0a +#define FS_88_2KHZ 0x0d +#define FS_96KHZ 0x0e +#define FS_176_4KHZ 0x11 +#define FS_192KHZ 0x12 +#define CM_MASK GENMASK(6, 5) /* For SRC Bypass mode */ +#define CM_0 (0x0 << 5) +#define CM_1 (0x1 << 5) +#define CM_2 (0x2 << 5) +#define CM_3 (0x3 << 5) +#define AK4375_06_DIGITAL_FILTER_SELECT 0x06 +#define DADFSEL BIT(5) /* 0: in SRC Bypass mode, 1: in SRC mode */ +#define DASL BIT(6) +#define DASD BIT(7) +#define AK4375_07_DAC_MONO_MIXING 0x07 +#define DACMUTE_MASK (GENMASK(5, 4) | GENMASK(1, 0)) /* Clear to mute */ +#define AK4375_08_JITTER_CLEANER_SETTING1 0x08 +#define AK4375_09_JITTER_CLEANER_SETTING2 0x09 +#define AK4375_0A_JITTER_CLEANER_SETTING3 0x0a +#define SELDAIN BIT(1) /* 0: SRC Bypass mode, 1: SRC mode */ +#define XCKSEL BIT(6) /* 0: PLL0, 1: MCKI */ +#define XCKCPSEL BIT(7) /* Should be equal to SELDAIN and XCKSEL */ +#define AK4375_0B_LCH_OUTPUT_VOLUME 0x0b +#define AK4375_0C_RCH_OUTPUT_VOLUME 0x0c +#define AK4375_0D_HP_VOLUME_CONTROL 0x0d +#define AK4375_0E_PLL_CLK_SOURCE_SELECT 0x0e +#define PLS BIT(0) /* 0: MCKI, 1: BCLK */ +#define AK4375_0F_PLL_REF_CLK_DIVIDER1 0x0f /* Reference clock divider [15:8] bits */ +#define AK4375_10_PLL_REF_CLK_DIVIDER2 0x10 /* Reference clock divider [7:0] bis */ +#define AK4375_11_PLL_FB_CLK_DIVIDER1 0x11 /* Feedback clock divider [15:8] bits */ +#define AK4375_12_PLL_FB_CLK_DIVIDER2 0x12 /* Feedback clock divider [7:0] bits */ +#define AK4375_13_SRC_CLK_SOURCE 0x13 /* SRC Bypass: SRCCKS=XCKSEL=SELDAIN=0 */ +#define SRCCKS BIT(0) /* SRC Clock source 0: MCKI, 1: PLL0 */ +#define DIV BIT(4) +#define AK4375_14_DAC_CLK_DIVIDER 0x14 +#define AK4375_15_AUDIO_IF_FORMAT 0x15 +#define DEVICEID_MASK GENMASK(7, 5) +#define AK4375_24_MODE_CONTROL 0x24 + +#define AK4375_PLL_FREQ_OUT_112896000 112896000 /* 44.1 kHz base rate */ +#define AK4375_PLL_FREQ_OUT_122880000 122880000 /* 32 and 48 kHz base rates */ + +#define DEVICEID_AK4375 0x00 +#define DEVICEID_AK4375A 0x01 +#define DEVICEID_AK4376A 0x02 +#define DEVICEID_AK4377 0x03 +#define DEVICEID_AK4331 0x07 + +static const char * const supply_names[] = { + "avdd", "tvdd" +}; + +struct ak4375_drvdata { + struct snd_soc_dai_driver *dai_drv; + const struct snd_soc_component_driver *comp_drv; +}; + +struct ak4375_priv { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *pdn_gpiod; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; + unsigned int rate; + unsigned int pld; + u8 mute_save; +}; + +static const struct reg_default ak4375_reg_defaults[] = { + { 0x00, 0x00 }, { 0x01, 0x00 }, { 0x02, 0x00 }, + { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 }, + { 0x06, 0x00 }, { 0x07, 0x00 }, { 0x08, 0x00 }, + { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x0b, 0x19 }, + { 0x0c, 0x19 }, { 0x0d, 0x75 }, { 0x0e, 0x01 }, + { 0x0f, 0x00 }, { 0x10, 0x00 }, { 0x11, 0x00 }, + { 0x12, 0x00 }, { 0x13, 0x00 }, { 0x14, 0x00 }, + { 0x15, 0x00 }, { 0x24, 0x00 }, +}; + +/* + * Output Digital volume control: + * from -12.5 to 3 dB in 0.5 dB steps (mute instead of -12.5 dB) + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -1250, 50, 0); + +/* + * HP-Amp Analog volume control: + * from -4.2 to 6 dB in 2 dB steps (mute instead of -4.2 dB) + */ +static DECLARE_TLV_DB_SCALE(hpg_tlv, -4200, 20, 0); + +static const char * const ak4375_ovolcn_select_texts[] = { "Dependent", "Independent" }; +static const char * const ak4375_mdac_select_texts[] = { "x1", "x1/2" }; +static const char * const ak4375_cpmode_select_texts[] = { + "Automatic Switching", + "+-VDD Operation", + "+-1/2VDD Operation" +}; + +/* + * DASD, DASL bits Digital Filter Setting + * 0, 0 : Sharp Roll-Off Filter + * 0, 1 : Slow Roll-Off Filter + * 1, 0 : Short delay Sharp Roll-Off Filter + * 1, 1 : Short delay Slow Roll-Off Filter + */ +static const char * const ak4375_digfil_select_texts[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short delay Sharp Roll-Off Filter", + "Short delay Slow Roll-Off Filter", +}; + +static const struct soc_enum ak4375_ovolcn_enum = + SOC_ENUM_SINGLE(AK4375_0B_LCH_OUTPUT_VOLUME, 7, + ARRAY_SIZE(ak4375_ovolcn_select_texts), ak4375_ovolcn_select_texts); +static const struct soc_enum ak4375_mdacl_enum = + SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 2, + ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts); +static const struct soc_enum ak4375_mdacr_enum = + SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 6, + ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts); +static const struct soc_enum ak4375_cpmode_enum = + SOC_ENUM_SINGLE(AK4375_03_POWER_MANAGEMENT4, 2, + ARRAY_SIZE(ak4375_cpmode_select_texts), ak4375_cpmode_select_texts); +static const struct soc_enum ak4375_digfil_enum = + SOC_ENUM_SINGLE(AK4375_06_DIGITAL_FILTER_SELECT, 6, + ARRAY_SIZE(ak4375_digfil_select_texts), ak4375_digfil_select_texts); + +static const struct snd_kcontrol_new ak4375_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Output Volume", AK4375_0B_LCH_OUTPUT_VOLUME, + AK4375_0C_RCH_OUTPUT_VOLUME, 0, 0x1f, 0, dac_tlv), + SOC_SINGLE_TLV("HP-Amp Analog Volume", + AK4375_0D_HP_VOLUME_CONTROL, 0, 0x1f, 0, hpg_tlv), + + SOC_DOUBLE("DAC Signal Invert Switch", AK4375_07_DAC_MONO_MIXING, 3, 7, 1, 0), + + SOC_ENUM("Digital Volume Control", ak4375_ovolcn_enum), + SOC_ENUM("DACL Signal Level", ak4375_mdacl_enum), + SOC_ENUM("DACR Signal Level", ak4375_mdacr_enum), + SOC_ENUM("Charge Pump Mode", ak4375_cpmode_enum), + SOC_ENUM("DAC Digital Filter Mode", ak4375_digfil_enum), +}; + +static const struct snd_kcontrol_new ak4375_hpl_mixer_controls[] = { + SOC_DAPM_SINGLE("LDACL Switch", AK4375_07_DAC_MONO_MIXING, 0, 1, 0), + SOC_DAPM_SINGLE("RDACL Switch", AK4375_07_DAC_MONO_MIXING, 1, 1, 0), +}; + +static const struct snd_kcontrol_new ak4375_hpr_mixer_controls[] = { + SOC_DAPM_SINGLE("LDACR Switch", AK4375_07_DAC_MONO_MIXING, 4, 1, 0), + SOC_DAPM_SINGLE("RDACR Switch", AK4375_07_DAC_MONO_MIXING, 5, 1, 0), +}; + +static int ak4375_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, PMPLL); + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, PMCP1); + usleep_range(6500, 7000); + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, PMLDO); + usleep_range(1000, 2000); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, PMCP2); + usleep_range(4500, 5000); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, 0x0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, 0x0); + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, 0x0); + snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, 0x0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget ak4375_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("DAC", NULL, AK4375_02_POWER_MANAGEMENT3, 0, 0, ak4375_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_IN("SDTI", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + + SND_SOC_DAPM_MIXER("HPR Mixer", AK4375_03_POWER_MANAGEMENT4, 1, 0, + &ak4375_hpr_mixer_controls[0], ARRAY_SIZE(ak4375_hpr_mixer_controls)), + SND_SOC_DAPM_MIXER("HPL Mixer", AK4375_03_POWER_MANAGEMENT4, 0, 0, + &ak4375_hpl_mixer_controls[0], ARRAY_SIZE(ak4375_hpl_mixer_controls)), +}; + +static const struct snd_soc_dapm_route ak4375_intercon[] = { + { "DAC", NULL, "SDTI" }, + + { "HPL Mixer", "LDACL Switch", "DAC" }, + { "HPL Mixer", "RDACL Switch", "DAC" }, + { "HPR Mixer", "LDACR Switch", "DAC" }, + { "HPR Mixer", "RDACR Switch", "DAC" }, + + { "HPL", NULL, "HPL Mixer" }, + { "HPR", NULL, "HPR Mixer" }, +}; + +static int ak4375_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component); + unsigned int freq_in, freq_out; + + ak4375->rate = params_rate(params); + + if (ak4375->rate <= 96000) + ak4375->pld = 0; + else + ak4375->pld = 1; + + freq_in = 32 * ak4375->rate / (ak4375->pld + 1); + + if ((ak4375->rate % 8000) == 0) + freq_out = AK4375_PLL_FREQ_OUT_122880000; + else + freq_out = AK4375_PLL_FREQ_OUT_112896000; + + return snd_soc_dai_set_pll(dai, 0, 0, freq_in, freq_out); +} + +static int ak4375_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component); + unsigned int mclk, plm, mdiv, div; + u8 cms, fs, cm; + + cms = snd_soc_component_read(component, AK4375_05_CLOCK_MODE_SELECT); + fs = cms & ~FS_MASK; + cm = cms & ~CM_MASK; + + switch (ak4375->rate) { + case 8000: + fs |= FS_8KHZ; + break; + case 11025: + fs |= FS_11_025KHZ; + break; + case 16000: + fs |= FS_16KHZ; + break; + case 22050: + fs |= FS_22_05KHZ; + break; + case 32000: + fs |= FS_32KHZ; + break; + case 44100: + fs |= FS_44_1KHZ; + break; + case 48000: + fs |= FS_48KHZ; + break; + case 88200: + fs |= FS_88_2KHZ; + break; + case 96000: + fs |= FS_96KHZ; + break; + case 176400: + fs |= FS_176_4KHZ; + break; + case 192000: + fs |= FS_192KHZ; + break; + default: + return -EINVAL; + } + + if (ak4375->rate <= 24000) { + cm |= CM_1; + mclk = 512 * ak4375->rate; + mdiv = freq_out / mclk - 1; + div = 0; + } else if (ak4375->rate <= 96000) { + cm |= CM_0; + mclk = 256 * ak4375->rate; + mdiv = freq_out / mclk - 1; + div = 0; + } else { + cm |= CM_3; + mclk = 128 * ak4375->rate; + mdiv = 4; + div = 1; + } + + /* Writing both fields in one go seems to make playback choppy on start */ + snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, FS_MASK, fs); + snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, CM_MASK, cm); + + snd_soc_component_write(component, AK4375_0F_PLL_REF_CLK_DIVIDER1, + (ak4375->pld & 0xff00) >> 8); + snd_soc_component_write(component, AK4375_10_PLL_REF_CLK_DIVIDER2, + ak4375->pld & 0x00ff); + + plm = freq_out / freq_in - 1; + snd_soc_component_write(component, AK4375_11_PLL_FB_CLK_DIVIDER1, (plm & 0xff00) >> 8); + snd_soc_component_write(component, AK4375_12_PLL_FB_CLK_DIVIDER2, plm & 0x00ff); + + snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, DIV, div); + + /* SRCCKS bit: force to 1 for SRC PLL source clock */ + snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, SRCCKS, SRCCKS); + + snd_soc_component_write(component, AK4375_14_DAC_CLK_DIVIDER, mdiv); + + dev_dbg(ak4375->dev, "rate=%d mclk=%d f_in=%d f_out=%d PLD=%d PLM=%d MDIV=%d DIV=%d\n", + ak4375->rate, mclk, freq_in, freq_out, ak4375->pld, plm, mdiv, div); + + return 0; +} + +static int ak4375_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component); + u8 val = snd_soc_component_read(component, AK4375_07_DAC_MONO_MIXING); + + dev_dbg(ak4375->dev, "mute=%d val=%d\n", mute, val); + + if (mute) { + ak4375->mute_save = val & DACMUTE_MASK; + val &= ~DACMUTE_MASK; + } else { + val |= ak4375->mute_save; + } + + snd_soc_component_write(component, AK4375_07_DAC_MONO_MIXING, val); + + return 0; +} + +#define AK4375_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +#define AK4375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops ak4375_dai_ops = { + .hw_params = ak4375_hw_params, + .mute_stream = ak4375_mute, + .set_pll = ak4375_dai_set_pll, +}; + +static struct snd_soc_dai_driver ak4375_dai = { + .name = "ak4375-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4375_RATES, + .rate_min = 8000, + .rate_max = 192000, + .formats = AK4375_FORMATS, + }, + .ops = &ak4375_dai_ops, +}; + +static void ak4375_power_off(struct ak4375_priv *ak4375) +{ + gpiod_set_value_cansleep(ak4375->pdn_gpiod, 0); + usleep_range(1000, 2000); + + regulator_bulk_disable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies); +} + +static int ak4375_power_on(struct ak4375_priv *ak4375) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies); + if (ret < 0) { + dev_err(ak4375->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + usleep_range(3000, 4000); + + gpiod_set_value_cansleep(ak4375->pdn_gpiod, 1); + usleep_range(1000, 2000); + + return 0; +} + +static int __maybe_unused ak4375_runtime_suspend(struct device *dev) +{ + struct ak4375_priv *ak4375 = dev_get_drvdata(dev); + + regcache_cache_only(ak4375->regmap, true); + ak4375_power_off(ak4375); + + return 0; +} + +static int __maybe_unused ak4375_runtime_resume(struct device *dev) +{ + struct ak4375_priv *ak4375 = dev_get_drvdata(dev); + int ret; + + ret = ak4375_power_on(ak4375); + if (ret < 0) + return ret; + + regcache_cache_only(ak4375->regmap, false); + regcache_mark_dirty(ak4375->regmap); + + return regcache_sync(ak4375->regmap); +} + +static const struct snd_soc_component_driver soc_codec_dev_ak4375 = { + .controls = ak4375_snd_controls, + .num_controls = ARRAY_SIZE(ak4375_snd_controls), + .dapm_widgets = ak4375_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4375_dapm_widgets), + .dapm_routes = ak4375_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4375_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config ak4375_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AK4375_24_MODE_CONTROL, + .reg_defaults = ak4375_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4375_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct ak4375_drvdata ak4375_drvdata = { + .dai_drv = &ak4375_dai, + .comp_drv = &soc_codec_dev_ak4375, +}; + +static const struct dev_pm_ops ak4375_pm = { + SET_RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static int ak4375_i2c_probe(struct i2c_client *i2c) +{ + struct ak4375_priv *ak4375; + const struct ak4375_drvdata *drvdata; + unsigned int deviceid; + int ret, i; + + ak4375 = devm_kzalloc(&i2c->dev, sizeof(*ak4375), GFP_KERNEL); + if (!ak4375) + return -ENOMEM; + + ak4375->regmap = devm_regmap_init_i2c(i2c, &ak4375_regmap); + if (IS_ERR(ak4375->regmap)) + return PTR_ERR(ak4375->regmap); + + i2c_set_clientdata(i2c, ak4375); + ak4375->dev = &i2c->dev; + + drvdata = of_device_get_match_data(&i2c->dev); + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + ak4375->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(ak4375->dev, ARRAY_SIZE(ak4375->supplies), ak4375->supplies); + if (ret < 0) { + dev_err(ak4375->dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ak4375->pdn_gpiod = devm_gpiod_get_optional(ak4375->dev, "pdn", GPIOD_OUT_LOW); + if (IS_ERR(ak4375->pdn_gpiod)) + return dev_err_probe(ak4375->dev, PTR_ERR(ak4375->pdn_gpiod), + "failed to get pdn\n"); + + ret = ak4375_power_on(ak4375); + if (ret < 0) + return ret; + + /* Don't read deviceid from cache */ + regcache_cache_bypass(ak4375->regmap, true); + + ret = regmap_read(ak4375->regmap, AK4375_15_AUDIO_IF_FORMAT, &deviceid); + if (ret < 0) { + dev_err(ak4375->dev, "unable to read DEVICEID!\n"); + return ret; + } + + regcache_cache_bypass(ak4375->regmap, false); + + deviceid = (deviceid & DEVICEID_MASK) >> 5; + + switch (deviceid) { + case DEVICEID_AK4331: + dev_err(ak4375->dev, "found untested AK4331\n"); + return -EINVAL; + case DEVICEID_AK4375: + dev_dbg(ak4375->dev, "found AK4375\n"); + break; + case DEVICEID_AK4375A: + dev_dbg(ak4375->dev, "found AK4375A\n"); + break; + case DEVICEID_AK4376A: + dev_err(ak4375->dev, "found unsupported AK4376/A!\n"); + return -EINVAL; + case DEVICEID_AK4377: + dev_err(ak4375->dev, "found unsupported AK4377!\n"); + return -EINVAL; + default: + dev_err(ak4375->dev, "unrecognized DEVICEID!\n"); + return -EINVAL; + } + + pm_runtime_set_active(ak4375->dev); + pm_runtime_enable(ak4375->dev); + + ret = devm_snd_soc_register_component(ak4375->dev, drvdata->comp_drv, + drvdata->dai_drv, 1); + if (ret < 0) { + dev_err(ak4375->dev, "Failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; +} + +static int ak4375_i2c_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); + + return 0; +} + +static const struct of_device_id ak4375_of_match[] = { + { .compatible = "asahi-kasei,ak4375", .data = &ak4375_drvdata }, + { }, +}; +MODULE_DEVICE_TABLE(of, ak4375_of_match); + +static struct i2c_driver ak4375_i2c_driver = { + .driver = { + .name = "ak4375", + .pm = &ak4375_pm, + .of_match_table = ak4375_of_match, + }, + .probe_new = ak4375_i2c_probe, + .remove = ak4375_i2c_remove, +}; +module_i2c_driver(ak4375_i2c_driver); + +MODULE_AUTHOR("Vincent Knecht <vincent.knecht@mailoo.org>"); +MODULE_DESCRIPTION("ASoC AK4375 DAC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index 7a5588f1df01..961a3e07e70f 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1311,7 +1311,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc"); classh = of_get_child_by_name(np, "cirrus,classh-internal-algo"); - classh_config->classh_algo_enable = classh ? true : false; + classh_config->classh_algo_enable = (classh != NULL); if (classh_config->classh_algo_enable) { classh_config->classh_bst_override = diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index d5fa8d2c4a70..faad5c638cb8 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -17,27 +17,13 @@ #include <linux/platform_device.h> #include <linux/slab.h> -#include <sound/cs35l41.h> #include "cs35l41.h" -static struct regmap_config cs35l41_regmap_i2c = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = CS35L41_REGSTRIDE, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .val_format_endian = REGMAP_ENDIAN_BIG, - .max_register = CS35L41_LASTREG, - .reg_defaults = cs35l41_reg, - .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), - .volatile_reg = cs35l41_volatile_reg, - .readable_reg = cs35l41_readable_reg, - .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, -}; - static const struct i2c_device_id cs35l41_id_i2c[] = { { "cs35l40", 0 }, { "cs35l41", 0 }, + { "cs35l51", 0 }, + { "cs35l53", 0 }, {} }; @@ -100,6 +86,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match); static struct i2c_driver cs35l41_i2c_driver = { .driver = { .name = "cs35l41", + .pm = &cs35l41_pm_ops, .of_match_table = of_match_ptr(cs35l41_of_match), .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, diff --git a/sound/soc/codecs/cs35l41-tables.c b/sound/soc/codecs/cs35l41-lib.c index 964e530afa27..e5a56bcbb223 100644 --- a/sound/soc/codecs/cs35l41-tables.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -1,17 +1,30 @@ // SPDX-License-Identifier: GPL-2.0 // -// cs35l41-tables.c -- CS35L41 ALSA SoC audio driver +// cs35l41-lib.c -- CS35L41 Common functions for HDA and ASoC Audio drivers // // Copyright 2017-2021 Cirrus Logic, Inc. // // Author: David Rhodes <david.rhodes@cirrus.com> +// Author: Lucas Tanure <lucas.tanure@cirrus.com> -#include "cs35l41.h" +#include <linux/dev_printk.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> -const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG] = { +#include <sound/cs35l41.h> + +static const struct reg_default cs35l41_reg[] = { { CS35L41_PWR_CTRL1, 0x00000000 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_PWR_CTRL3, 0x01000010 }, { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, + { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, + { CS35L41_TST_FS_MON0, 0x00020016 }, + { CS35L41_BSTCVRT_COEFF, 0x00002424 }, + { CS35L41_BSTCVRT_SLOPE_LBST, 0x00007500 }, + { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A }, { CS35L41_SP_ENABLES, 0x00000000 }, { CS35L41_SP_RATE_CTRL, 0x00000028 }, { CS35L41_SP_FORMAT, 0x18180200 }, @@ -39,15 +52,20 @@ const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG] = { { CS35L41_CLASSH_CFG, 0x000B0405 }, { CS35L41_WKFET_CFG, 0x00000111 }, { CS35L41_NG_CFG, 0x00000033 }, - { CS35L41_AMP_GAIN_CTRL, 0x00000273 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, + { CS35L41_IRQ1_MASK1, 0xFFFFFFFF }, + { CS35L41_IRQ1_MASK2, 0xFFFFFFFF }, + { CS35L41_IRQ1_MASK3, 0xFFFF87FF }, + { CS35L41_IRQ1_MASK4, 0xFEFFFFFF }, { CS35L41_GPIO1_CTRL1, 0xE1000001 }, { CS35L41_GPIO2_CTRL1, 0xE1000001 }, { CS35L41_MIXER_NGATE_CFG, 0x00000000 }, { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 }, { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 }, + { CS35L41_DSP1_CCM_CORE_CTRL, 0x00000101 }, }; -bool cs35l41_readable_reg(struct device *dev, unsigned int reg) +static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { case CS35L41_DEVID: @@ -72,10 +90,14 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_PROTECT_REL_ERR_IGN: case CS35L41_GPIO_PAD_CONTROL: case CS35L41_JTAG_CONTROL: + case CS35L41_PWRMGT_CTL: + case CS35L41_WAKESRC_CTL: + case CS35L41_PWRMGT_STS: case CS35L41_PLL_CLK_CTRL: case CS35L41_DSP_CLK_CTRL: case CS35L41_GLOBAL_CLK_CTRL: case CS35L41_DATA_FS_SEL: + case CS35L41_TST_FS_MON0: case CS35L41_MDSYNC_EN: case CS35L41_MDSYNC_TX_ID: case CS35L41_MDSYNC_PWR_CTRL: @@ -200,6 +222,83 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_DIE_STS2: case CS35L41_TEMP_CAL1: case CS35L41_TEMP_CAL2: + case CS35L41_DSP1_TIMESTAMP_COUNT: + case CS35L41_DSP1_SYS_ID: + case CS35L41_DSP1_SYS_VERSION: + case CS35L41_DSP1_SYS_CORE_ID: + case CS35L41_DSP1_SYS_AHB_ADDR: + case CS35L41_DSP1_SYS_XSRAM_SIZE: + case CS35L41_DSP1_SYS_YSRAM_SIZE: + case CS35L41_DSP1_SYS_PSRAM_SIZE: + case CS35L41_DSP1_SYS_PM_BOOT_SIZE: + case CS35L41_DSP1_SYS_FEATURES: + case CS35L41_DSP1_SYS_FIR_FILTERS: + case CS35L41_DSP1_SYS_LMS_FILTERS: + case CS35L41_DSP1_SYS_XM_BANK_SIZE: + case CS35L41_DSP1_SYS_YM_BANK_SIZE: + case CS35L41_DSP1_SYS_PM_BANK_SIZE: + case CS35L41_DSP1_RX1_RATE: + case CS35L41_DSP1_RX2_RATE: + case CS35L41_DSP1_RX3_RATE: + case CS35L41_DSP1_RX4_RATE: + case CS35L41_DSP1_RX5_RATE: + case CS35L41_DSP1_RX6_RATE: + case CS35L41_DSP1_RX7_RATE: + case CS35L41_DSP1_RX8_RATE: + case CS35L41_DSP1_TX1_RATE: + case CS35L41_DSP1_TX2_RATE: + case CS35L41_DSP1_TX3_RATE: + case CS35L41_DSP1_TX4_RATE: + case CS35L41_DSP1_TX5_RATE: + case CS35L41_DSP1_TX6_RATE: + case CS35L41_DSP1_TX7_RATE: + case CS35L41_DSP1_TX8_RATE: + case CS35L41_DSP1_SCRATCH1: + case CS35L41_DSP1_SCRATCH2: + case CS35L41_DSP1_SCRATCH3: + case CS35L41_DSP1_SCRATCH4: + case CS35L41_DSP1_CCM_CORE_CTRL: + case CS35L41_DSP1_CCM_CLK_OVERRIDE: + case CS35L41_DSP1_XM_MSTR_EN: + case CS35L41_DSP1_XM_CORE_PRI: + case CS35L41_DSP1_XM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_XM_AHB_UP_PL_PRI: + case CS35L41_DSP1_XM_ACCEL_PL0_PRI: + case CS35L41_DSP1_XM_NPL0_PRI: + case CS35L41_DSP1_YM_MSTR_EN: + case CS35L41_DSP1_YM_CORE_PRI: + case CS35L41_DSP1_YM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_YM_AHB_UP_PL_PRI: + case CS35L41_DSP1_YM_ACCEL_PL0_PRI: + case CS35L41_DSP1_YM_NPL0_PRI: + case CS35L41_DSP1_MPU_XM_ACCESS0: + case CS35L41_DSP1_MPU_YM_ACCESS0: + case CS35L41_DSP1_MPU_WNDW_ACCESS0: + case CS35L41_DSP1_MPU_XREG_ACCESS0: + case CS35L41_DSP1_MPU_YREG_ACCESS0: + case CS35L41_DSP1_MPU_XM_ACCESS1: + case CS35L41_DSP1_MPU_YM_ACCESS1: + case CS35L41_DSP1_MPU_WNDW_ACCESS1: + case CS35L41_DSP1_MPU_XREG_ACCESS1: + case CS35L41_DSP1_MPU_YREG_ACCESS1: + case CS35L41_DSP1_MPU_XM_ACCESS2: + case CS35L41_DSP1_MPU_YM_ACCESS2: + case CS35L41_DSP1_MPU_WNDW_ACCESS2: + case CS35L41_DSP1_MPU_XREG_ACCESS2: + case CS35L41_DSP1_MPU_YREG_ACCESS2: + case CS35L41_DSP1_MPU_XM_ACCESS3: + case CS35L41_DSP1_MPU_YM_ACCESS3: + case CS35L41_DSP1_MPU_WNDW_ACCESS3: + case CS35L41_DSP1_MPU_XREG_ACCESS3: + case CS35L41_DSP1_MPU_YREG_ACCESS3: + case CS35L41_DSP1_MPU_XM_VIO_ADDR: + case CS35L41_DSP1_MPU_XM_VIO_STATUS: + case CS35L41_DSP1_MPU_YM_VIO_ADDR: + case CS35L41_DSP1_MPU_YM_VIO_STATUS: + case CS35L41_DSP1_MPU_PM_VIO_ADDR: + case CS35L41_DSP1_MPU_PM_VIO_STATUS: + case CS35L41_DSP1_MPU_LOCK_CONFIG: + case CS35L41_DSP1_MPU_WDT_RST_CTRL: case CS35L41_OTP_TRIM_1: case CS35L41_OTP_TRIM_2: case CS35L41_OTP_TRIM_3: @@ -237,6 +336,13 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_OTP_TRIM_35: case CS35L41_OTP_TRIM_36: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: + case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: /*test regs*/ case CS35L41_PLL_OVR: case CS35L41_BST_TEST_DUTY: @@ -247,23 +353,35 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg) } } -bool cs35l41_precious_reg(struct device *dev, unsigned int reg) +static bool cs35l41_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_TST_FS_MON0: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: return true; default: return false; } } -bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) +static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case CS35L41_DEVID: case CS35L41_SFT_RESET: case CS35L41_FABID: case CS35L41_REVID: + case CS35L41_OTPID: + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: + case CS35L41_PWRMGT_CTL: + case CS35L41_WAKESRC_CTL: + case CS35L41_PWRMGT_STS: case CS35L41_DTEMP_EN: case CS35L41_IRQ1_STATUS: case CS35L41_IRQ1_STATUS1: @@ -274,17 +392,6 @@ bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_IRQ1_RAW_STATUS2: case CS35L41_IRQ1_RAW_STATUS3: case CS35L41_IRQ1_RAW_STATUS4: - case CS35L41_IRQ1_FRC1: - case CS35L41_IRQ1_FRC2: - case CS35L41_IRQ1_FRC3: - case CS35L41_IRQ1_FRC4: - case CS35L41_IRQ1_EDGE1: - case CS35L41_IRQ1_EDGE4: - case CS35L41_IRQ1_POL1: - case CS35L41_IRQ1_POL2: - case CS35L41_IRQ1_POL3: - case CS35L41_IRQ1_POL4: - case CS35L41_IRQ1_DB3: case CS35L41_IRQ2_STATUS: case CS35L41_IRQ2_STATUS1: case CS35L41_IRQ2_STATUS2: @@ -294,54 +401,20 @@ bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_IRQ2_RAW_STATUS2: case CS35L41_IRQ2_RAW_STATUS3: case CS35L41_IRQ2_RAW_STATUS4: - case CS35L41_IRQ2_FRC1: - case CS35L41_IRQ2_FRC2: - case CS35L41_IRQ2_FRC3: - case CS35L41_IRQ2_FRC4: - case CS35L41_IRQ2_EDGE1: - case CS35L41_IRQ2_EDGE4: - case CS35L41_IRQ2_POL1: - case CS35L41_IRQ2_POL2: - case CS35L41_IRQ2_POL3: - case CS35L41_IRQ2_POL4: - case CS35L41_IRQ2_DB3: case CS35L41_GPIO_STATUS1: - case CS35L41_OTP_TRIM_1: - case CS35L41_OTP_TRIM_2: - case CS35L41_OTP_TRIM_3: - case CS35L41_OTP_TRIM_4: - case CS35L41_OTP_TRIM_5: - case CS35L41_OTP_TRIM_6: - case CS35L41_OTP_TRIM_7: - case CS35L41_OTP_TRIM_8: - case CS35L41_OTP_TRIM_9: - case CS35L41_OTP_TRIM_10: - case CS35L41_OTP_TRIM_11: - case CS35L41_OTP_TRIM_12: - case CS35L41_OTP_TRIM_13: - case CS35L41_OTP_TRIM_14: - case CS35L41_OTP_TRIM_15: - case CS35L41_OTP_TRIM_16: - case CS35L41_OTP_TRIM_17: - case CS35L41_OTP_TRIM_18: - case CS35L41_OTP_TRIM_19: - case CS35L41_OTP_TRIM_20: - case CS35L41_OTP_TRIM_21: - case CS35L41_OTP_TRIM_22: - case CS35L41_OTP_TRIM_23: - case CS35L41_OTP_TRIM_24: - case CS35L41_OTP_TRIM_25: - case CS35L41_OTP_TRIM_26: - case CS35L41_OTP_TRIM_27: - case CS35L41_OTP_TRIM_28: - case CS35L41_OTP_TRIM_29: - case CS35L41_OTP_TRIM_30: - case CS35L41_OTP_TRIM_31: - case CS35L41_OTP_TRIM_32: - case CS35L41_OTP_TRIM_33: - case CS35L41_OTP_TRIM_34: - case CS35L41_OTP_TRIM_35: - case CS35L41_OTP_TRIM_36: + case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: + case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: + case CS35L41_DSP1_SCRATCH1: + case CS35L41_DSP1_SCRATCH2: + case CS35L41_DSP1_SCRATCH3: + case CS35L41_DSP1_SCRATCH4: + case CS35L41_DSP1_CCM_CLK_OVERRIDE ... CS35L41_DSP1_WDT_STATUS: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: return true; default: @@ -555,7 +628,46 @@ static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ }; -const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] = { +static const struct reg_sequence cs35l41_reva0_errata_patch[] = { + { 0x00003854, 0x05180240 }, + { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, + { 0x00004310, 0x00000000 }, + { CS35L41_VPVBST_FS_SEL, 0x00000000 }, + { CS35L41_OTP_TRIM_30, 0x9091A1C8 }, + { 0x00003014, 0x0200EE0E }, + { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, + { 0x00000054, 0x00000004 }, + { CS35L41_IRQ1_DB3, 0x00000000 }, + { CS35L41_IRQ2_DB3, 0x00000000 }, + { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, +}; + +static const struct reg_sequence cs35l41_revb0_errata_patch[] = { + { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, + { 0x00004310, 0x00000000 }, + { CS35L41_VPVBST_FS_SEL, 0x00000000 }, + { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, + { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, +}; + +static const struct reg_sequence cs35l41_revb2_errata_patch[] = { + { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, + { 0x00004310, 0x00000000 }, + { CS35L41_VPVBST_FS_SEL, 0x00000000 }, + { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, + { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, +}; + +static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = { { .id = 0x01, .map = otp_map_1, @@ -592,3 +704,337 @@ const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] .word_offset = 2, }, }; + +struct regmap_config cs35l41_regmap_i2c = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = CS35L41_REGSTRIDE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L41_LASTREG, + .reg_defaults = cs35l41_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), + .volatile_reg = cs35l41_volatile_reg, + .readable_reg = cs35l41_readable_reg, + .precious_reg = cs35l41_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c); + +struct regmap_config cs35l41_regmap_spi = { + .reg_bits = 32, + .val_bits = 32, + .pad_bits = 16, + .reg_stride = CS35L41_REGSTRIDE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L41_LASTREG, + .reg_defaults = cs35l41_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), + .volatile_reg = cs35l41_volatile_reg, + .readable_reg = cs35l41_readable_reg, + .precious_reg = cs35l41_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(cs35l41_regmap_spi); + +static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) { + if (cs35l41_otp_map_map[i].id == otp_id) + return &cs35l41_otp_map_map[i]; + } + + return NULL; +} + +int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap) +{ + static const struct reg_sequence unlock[] = { + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, + }; + int ret; + + ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock)); + if (ret) + dev_err(dev, "Failed to unlock test key: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_test_key_unlock); + +int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap) +{ + static const struct reg_sequence unlock[] = { + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, + }; + int ret; + + ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock)); + if (ret) + dev_err(dev, "Failed to lock test key: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_test_key_lock); + +/* Must be called with the TEST_KEY unlocked */ +int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) +{ + const struct cs35l41_otp_map_element_t *otp_map_match; + const struct cs35l41_otp_packed_element_t *otp_map; + int bit_offset, word_offset, ret, i; + unsigned int bit_sum = 8; + u32 otp_val, otp_id_reg; + u32 *otp_mem; + + otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL); + if (!otp_mem) + return -ENOMEM; + + ret = regmap_read(regmap, CS35L41_OTPID, &otp_id_reg); + if (ret) { + dev_err(dev, "Read OTP ID failed: %d\n", ret); + goto err_otp_unpack; + } + + otp_map_match = cs35l41_find_otp_map(otp_id_reg); + + if (!otp_map_match) { + dev_err(dev, "OTP Map matching ID %d not found\n", otp_id_reg); + ret = -EINVAL; + goto err_otp_unpack; + } + + ret = regmap_bulk_read(regmap, CS35L41_OTP_MEM0, otp_mem, CS35L41_OTP_SIZE_WORDS); + if (ret) { + dev_err(dev, "Read OTP Mem failed: %d\n", ret); + goto err_otp_unpack; + } + + otp_map = otp_map_match->map; + + bit_offset = otp_map_match->bit_offset; + word_offset = otp_map_match->word_offset; + + for (i = 0; i < otp_map_match->num_elements; i++) { + dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n", + bit_offset, word_offset, bit_sum % 32); + if (bit_offset + otp_map[i].size - 1 >= 32) { + otp_val = (otp_mem[word_offset] & + GENMASK(31, bit_offset)) >> bit_offset; + otp_val |= (otp_mem[++word_offset] & + GENMASK(bit_offset + otp_map[i].size - 33, 0)) << + (32 - bit_offset); + bit_offset += otp_map[i].size - 32; + } else { + otp_val = (otp_mem[word_offset] & + GENMASK(bit_offset + otp_map[i].size - 1, bit_offset) + ) >> bit_offset; + bit_offset += otp_map[i].size; + } + bit_sum += otp_map[i].size; + + if (bit_offset == 32) { + bit_offset = 0; + word_offset++; + } + + if (otp_map[i].reg != 0) { + ret = regmap_update_bits(regmap, otp_map[i].reg, + GENMASK(otp_map[i].shift + otp_map[i].size - 1, + otp_map[i].shift), + otp_val << otp_map[i].shift); + if (ret < 0) { + dev_err(dev, "Write OTP val failed: %d\n", ret); + goto err_otp_unpack; + } + } + } + + ret = 0; + +err_otp_unpack: + kfree(otp_mem); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_otp_unpack); + +/* Must be called with the TEST_KEY unlocked */ +int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid) +{ + char *rev; + int ret; + + switch (reg_revid) { + case CS35L41_REVID_A0: + ret = regmap_register_patch(reg, cs35l41_reva0_errata_patch, + ARRAY_SIZE(cs35l41_reva0_errata_patch)); + rev = "A0"; + break; + case CS35L41_REVID_B0: + ret = regmap_register_patch(reg, cs35l41_revb0_errata_patch, + ARRAY_SIZE(cs35l41_revb0_errata_patch)); + rev = "B0"; + break; + case CS35L41_REVID_B2: + ret = regmap_register_patch(reg, cs35l41_revb2_errata_patch, + ARRAY_SIZE(cs35l41_revb2_errata_patch)); + rev = "B2"; + break; + default: + ret = -EINVAL; + rev = "XX"; + break; + } + + if (ret) + dev_err(dev, "Failed to apply %s errata patch: %d\n", rev, ret); + + ret = regmap_write(reg, CS35L41_DSP1_CCM_CORE_CTRL, 0); + if (ret < 0) + dev_err(dev, "Write CCM_CORE_CTRL failed: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch); + +int cs35l41_set_channels(struct device *dev, struct regmap *reg, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + unsigned int val, mask; + int i; + + if (tx_num > 4 || rx_num > 2) + return -EINVAL; + + val = 0; + mask = 0; + for (i = 0; i < rx_num; i++) { + dev_dbg(dev, "rx slot %d position = %d\n", i, rx_slot[i]); + val |= rx_slot[i] << (i * 8); + mask |= 0x3F << (i * 8); + } + regmap_update_bits(reg, CS35L41_SP_FRAME_RX_SLOT, mask, val); + + val = 0; + mask = 0; + for (i = 0; i < tx_num; i++) { + dev_dbg(dev, "tx slot %d position = %d\n", i, tx_slot[i]); + val |= tx_slot[i] << (i * 8); + mask |= 0x3F << (i * 8); + } + regmap_update_bits(reg, CS35L41_SP_FRAME_TX_SLOT, mask, val); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l41_set_channels); + +static const unsigned char cs35l41_bst_k1_table[4][5] = { + { 0x24, 0x32, 0x32, 0x4F, 0x57 }, + { 0x24, 0x32, 0x32, 0x4F, 0x57 }, + { 0x40, 0x32, 0x32, 0x4F, 0x57 }, + { 0x40, 0x32, 0x32, 0x4F, 0x57 } +}; + +static const unsigned char cs35l41_bst_k2_table[4][5] = { + { 0x24, 0x49, 0x66, 0xA3, 0xEA }, + { 0x24, 0x49, 0x66, 0xA3, 0xEA }, + { 0x48, 0x49, 0x66, 0xA3, 0xEA }, + { 0x48, 0x49, 0x66, 0xA3, 0xEA } +}; + +static const unsigned char cs35l41_bst_slope_table[4] = { + 0x75, 0x6B, 0x3B, 0x28 +}; + + +int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind, int boost_cap, + int boost_ipk) +{ + unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled; + int ret; + + switch (boost_ind) { + case 1000: /* 1.0 uH */ + bst_lbst_val = 0; + break; + case 1200: /* 1.2 uH */ + bst_lbst_val = 1; + break; + case 1500: /* 1.5 uH */ + bst_lbst_val = 2; + break; + case 2200: /* 2.2 uH */ + bst_lbst_val = 3; + break; + default: + dev_err(dev, "Invalid boost inductor value: %d nH\n", boost_ind); + return -EINVAL; + } + + switch (boost_cap) { + case 0 ... 19: + bst_cbst_range = 0; + break; + case 20 ... 50: + bst_cbst_range = 1; + break; + case 51 ... 100: + bst_cbst_range = 2; + break; + case 101 ... 200: + bst_cbst_range = 3; + break; + default: /* 201 uF and greater */ + bst_cbst_range = 4; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF, + CS35L41_BST_K1_MASK | CS35L41_BST_K2_MASK, + cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range] + << CS35L41_BST_K1_SHIFT | + cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range] + << CS35L41_BST_K2_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost coefficients: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST, + CS35L41_BST_SLOPE_MASK | CS35L41_BST_LBST_VAL_MASK, + cs35l41_bst_slope_table[bst_lbst_val] + << CS35L41_BST_SLOPE_SHIFT | + bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost slope/inductor value: %d\n", ret); + return ret; + } + + if (boost_ipk < 1600 || boost_ipk > 4500) { + dev_err(dev, "Invalid boost inductor peak current: %d mA\n", boost_ipk); + return -EINVAL; + } + bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10; + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR, CS35L41_BST_IPK_MASK, + bst_ipk_scaled << CS35L41_BST_IPK_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost inductor peak current: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l41_boost_config); + +MODULE_DESCRIPTION("CS35L41 library"); +MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>"); +MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c index 3fa99741779a..6dfd5459aa20 100644 --- a/sound/soc/codecs/cs35l41-spi.c +++ b/sound/soc/codecs/cs35l41-spi.c @@ -15,28 +15,13 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> -#include <sound/cs35l41.h> #include "cs35l41.h" -static struct regmap_config cs35l41_regmap_spi = { - .reg_bits = 32, - .val_bits = 32, - .pad_bits = 16, - .reg_stride = CS35L41_REGSTRIDE, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .val_format_endian = REGMAP_ENDIAN_BIG, - .max_register = CS35L41_LASTREG, - .reg_defaults = cs35l41_reg, - .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), - .volatile_reg = cs35l41_volatile_reg, - .readable_reg = cs35l41_readable_reg, - .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, -}; - static const struct spi_device_id cs35l41_id_spi[] = { { "cs35l40", 0 }, { "cs35l41", 0 }, + { "cs35l51", 0 }, + { "cs35l53", 0 }, {} }; @@ -99,6 +84,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match); static struct spi_driver cs35l41_spi_driver = { .driver = { .name = "cs35l41", + .pm = &cs35l41_pm_ops, .of_match_table = of_match_ptr(cs35l41_of_match), .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 9c4d481f7614..77a017694645 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -13,8 +13,8 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/of_device.h> +#include <linux/pm_runtime.h> #include <linux/property.h> -#include <linux/slab.h> #include <sound/initval.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -151,24 +151,6 @@ static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = { { 6144000, 16, 24 }, }; -static const unsigned char cs35l41_bst_k1_table[4][5] = { - { 0x24, 0x32, 0x32, 0x4F, 0x57 }, - { 0x24, 0x32, 0x32, 0x4F, 0x57 }, - { 0x40, 0x32, 0x32, 0x4F, 0x57 }, - { 0x40, 0x32, 0x32, 0x4F, 0x57 } -}; - -static const unsigned char cs35l41_bst_k2_table[4][5] = { - { 0x24, 0x49, 0x66, 0xA3, 0xEA }, - { 0x24, 0x49, 0x66, 0xA3, 0xEA }, - { 0x48, 0x49, 0x66, 0xA3, 0xEA }, - { 0x48, 0x49, 0x66, 0xA3, 0xEA } -}; - -static const unsigned char cs35l41_bst_slope_table[4] = { - 0x75, 0x6B, 0x3B, 0x28 -}; - static int cs35l41_get_fs_mon_config_index(int freq) { int i; @@ -197,6 +179,134 @@ static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L41_AMP_DIG_VOL_CTRL, 0, cs35l41_pcm_sftramp_text); +static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (cs35l41->dsp.cs_dsp.booted) + return 0; + + return wm_adsp_early_event(w, kcontrol, event); + case SND_SOC_DAPM_PRE_PMD: + if (cs35l41->dsp.preloaded) + return 0; + + if (cs35l41->dsp.cs_dsp.running) { + ret = wm_adsp_event(w, kcontrol, event); + if (ret) + return ret; + } + + return wm_adsp_early_event(w, kcontrol, event); + default: + return 0; + } +} + +static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd, + enum cs35l41_cspl_mbox_status sts) +{ + switch (cmd) { + case CSPL_MBOX_CMD_NONE: + case CSPL_MBOX_CMD_UNKNOWN_CMD: + return true; + case CSPL_MBOX_CMD_PAUSE: + case CSPL_MBOX_CMD_OUT_OF_HIBERNATE: + return (sts == CSPL_MBOX_STS_PAUSED); + case CSPL_MBOX_CMD_RESUME: + return (sts == CSPL_MBOX_STS_RUNNING); + case CSPL_MBOX_CMD_REINIT: + return (sts == CSPL_MBOX_STS_RUNNING); + case CSPL_MBOX_CMD_STOP_PRE_REINIT: + return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT); + default: + return false; + } +} + +static int cs35l41_set_cspl_mbox_cmd(struct cs35l41_private *cs35l41, + enum cs35l41_cspl_mbox_cmd cmd) +{ + unsigned int sts = 0, i; + int ret; + + // Set mailbox cmd + ret = regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd); + if (ret < 0) { + if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE) + dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret); + return ret; + } + + // Read mailbox status and verify it is appropriate for the given cmd + for (i = 0; i < 5; i++) { + usleep_range(1000, 1100); + + ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &sts); + if (ret < 0) { + dev_err(cs35l41->dev, "Failed to read MBOX STS: %d\n", ret); + continue; + } + + if (!cs35l41_check_cspl_mbox_sts(cmd, sts)) { + dev_dbg(cs35l41->dev, + "[%u] cmd %u returned invalid sts %u", + i, cmd, sts); + } else { + return 0; + } + } + + dev_err(cs35l41->dev, + "Failed to set mailbox cmd %u (status %u)\n", + cmd, sts); + + return -ENOMSG; +} + +static int cs35l41_dsp_audio_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + unsigned int fw_status; + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!cs35l41->dsp.cs_dsp.running) + return wm_adsp_event(w, kcontrol, event); + + ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &fw_status); + if (ret < 0) { + dev_err(cs35l41->dev, + "Failed to read firmware status: %d\n", ret); + return ret; + } + + switch (fw_status) { + case CSPL_MBOX_STS_RUNNING: + case CSPL_MBOX_STS_PAUSED: + break; + default: + dev_err(cs35l41->dev, "Firmware status is invalid: %u\n", + fw_status); + return -EINVAL; + } + + return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_RESUME); + case SND_SOC_DAPM_PRE_PMD: + return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_PAUSE); + default: + return 0; + } +} + static const char * const cs35l41_pcm_source_texts[] = {"ASP", "DSP"}; static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32}; static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum, @@ -255,6 +365,24 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum, static const struct snd_kcontrol_new asp_tx4_mux = SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum); +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx1_enum, + CS35L41_DSP1_RX1_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new dsp_rx1_mux = + SOC_DAPM_ENUM("DSPRX1 SRC", cs35l41_dsprx1_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx2_enum, + CS35L41_DSP1_RX2_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new dsp_rx2_mux = + SOC_DAPM_ENUM("DSPRX2 SRC", cs35l41_dsprx2_enum); + static const struct snd_kcontrol_new cs35l41_aud_controls[] = { SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L41_AMP_DIG_VOL_CTRL, 3, 0x4CF, 0x391, dig_vol_tlv), @@ -282,130 +410,10 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = { CS35L41_AMP_INV_PCM_SHIFT, 1, 0), SOC_SINGLE("Amp Gain ZC", CS35L41_AMP_GAIN_CTRL, CS35L41_AMP_GAIN_ZC_SHIFT, 1, 0), + WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + WM_ADSP_FW_CONTROL("DSP1", 0), }; -static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) { - if (cs35l41_otp_map_map[i].id == otp_id) - return &cs35l41_otp_map_map[i]; - } - - return NULL; -} - -static int cs35l41_otp_unpack(void *data) -{ - const struct cs35l41_otp_map_element_t *otp_map_match; - const struct cs35l41_otp_packed_element_t *otp_map; - struct cs35l41_private *cs35l41 = data; - int bit_offset, word_offset, ret, i; - unsigned int bit_sum = 8; - u32 otp_val, otp_id_reg; - u32 *otp_mem; - - otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL); - if (!otp_mem) - return -ENOMEM; - - ret = regmap_read(cs35l41->regmap, CS35L41_OTPID, &otp_id_reg); - if (ret < 0) { - dev_err(cs35l41->dev, "Read OTP ID failed: %d\n", ret); - goto err_otp_unpack; - } - - otp_map_match = cs35l41_find_otp_map(otp_id_reg); - - if (!otp_map_match) { - dev_err(cs35l41->dev, "OTP Map matching ID %d not found\n", - otp_id_reg); - ret = -EINVAL; - goto err_otp_unpack; - } - - ret = regmap_bulk_read(cs35l41->regmap, CS35L41_OTP_MEM0, otp_mem, - CS35L41_OTP_SIZE_WORDS); - if (ret < 0) { - dev_err(cs35l41->dev, "Read OTP Mem failed: %d\n", ret); - goto err_otp_unpack; - } - - otp_map = otp_map_match->map; - - bit_offset = otp_map_match->bit_offset; - word_offset = otp_map_match->word_offset; - - ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000055); - if (ret < 0) { - dev_err(cs35l41->dev, "Write Unlock key failed 1/2: %d\n", ret); - goto err_otp_unpack; - } - ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000AA); - if (ret < 0) { - dev_err(cs35l41->dev, "Write Unlock key failed 2/2: %d\n", ret); - goto err_otp_unpack; - } - - for (i = 0; i < otp_map_match->num_elements; i++) { - dev_dbg(cs35l41->dev, - "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n", - bit_offset, word_offset, bit_sum % 32); - if (bit_offset + otp_map[i].size - 1 >= 32) { - otp_val = (otp_mem[word_offset] & - GENMASK(31, bit_offset)) >> - bit_offset; - otp_val |= (otp_mem[++word_offset] & - GENMASK(bit_offset + - otp_map[i].size - 33, 0)) << - (32 - bit_offset); - bit_offset += otp_map[i].size - 32; - } else { - otp_val = (otp_mem[word_offset] & - GENMASK(bit_offset + otp_map[i].size - 1, - bit_offset)) >> bit_offset; - bit_offset += otp_map[i].size; - } - bit_sum += otp_map[i].size; - - if (bit_offset == 32) { - bit_offset = 0; - word_offset++; - } - - if (otp_map[i].reg != 0) { - ret = regmap_update_bits(cs35l41->regmap, - otp_map[i].reg, - GENMASK(otp_map[i].shift + - otp_map[i].size - 1, - otp_map[i].shift), - otp_val << otp_map[i].shift); - if (ret < 0) { - dev_err(cs35l41->dev, "Write OTP val failed: %d\n", - ret); - goto err_otp_unpack; - } - } - } - - ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000CC); - if (ret < 0) { - dev_err(cs35l41->dev, "Write Lock key failed 1/2: %d\n", ret); - goto err_otp_unpack; - } - ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000033); - if (ret < 0) { - dev_err(cs35l41->dev, "Write Lock key failed 2/2: %d\n", ret); - goto err_otp_unpack; - } - ret = 0; - -err_otp_unpack: - kfree(otp_mem); - return ret; -} - static irqreturn_t cs35l41_irq(int irq, void *data) { struct cs35l41_private *cs35l41 = data; @@ -414,6 +422,8 @@ static irqreturn_t cs35l41_irq(int irq, void *data) int ret = IRQ_NONE; unsigned int i; + pm_runtime_get_sync(cs35l41->dev); + for (i = 0; i < ARRAY_SIZE(status); i++) { regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE), @@ -426,7 +436,7 @@ static irqreturn_t cs35l41_irq(int irq, void *data) /* Check to see if unmasked bits are active */ if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) && !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) - return IRQ_NONE; + goto done; if (status[3] & CS35L41_OTP_BOOT_DONE) { regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4, @@ -531,23 +541,27 @@ static irqreturn_t cs35l41_irq(int irq, void *data) ret = IRQ_HANDLED; } +done: + pm_runtime_mark_last_busy(cs35l41->dev); + pm_runtime_put_autosuspend(cs35l41->dev); + return ret; } static const struct reg_sequence cs35l41_pup_patch[] = { - { 0x00000040, 0x00000055 }, - { 0x00000040, 0x000000AA }, + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, { 0x00002084, 0x002F1AA0 }, - { 0x00000040, 0x000000CC }, - { 0x00000040, 0x00000033 }, + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, }; static const struct reg_sequence cs35l41_pdn_patch[] = { - { 0x00000040, 0x00000055 }, - { 0x00000040, 0x000000AA }, + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, { 0x00002084, 0x002F1AA3 }, - { 0x00000040, 0x000000CC }, - { 0x00000040, 0x00000033 }, + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, }; static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, @@ -596,6 +610,14 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, } static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { + SND_SOC_DAPM_SPK("DSP1 Preload", NULL), + SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0, + cs35l41_dsp_preload_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, + cs35l41_dsp_audio_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("SPK"), SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0), @@ -611,11 +633,18 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { SND_SOC_DAPM_SIGGEN("VBST"), SND_SOC_DAPM_SIGGEN("TEMP"), - SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L41_PWR_CTRL2, 12, 0), - SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L41_PWR_CTRL2, 13, 0), - SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L41_PWR_CTRL2, 8, 0), - SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L41_PWR_CTRL2, 9, 0), - SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, CS35L41_PWR_CTRL2, 10, 0), + SND_SOC_DAPM_SUPPLY("VMON", CS35L41_PWR_CTRL2, 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("IMON", CS35L41_PWR_CTRL2, 13, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VPMON", CS35L41_PWR_CTRL2, 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VBSTMON", CS35L41_PWR_CTRL2, 9, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TEMPMON", CS35L41_PWR_CTRL2, 10, 0, NULL, 0), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("IMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L41_PWR_CTRL3, 4, 0), SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0, @@ -626,33 +655,51 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux), SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux), SND_SOC_DAPM_MUX("ASP TX4 Source", SND_SOC_NOPM, 0, 0, &asp_tx4_mux), + SND_SOC_DAPM_MUX("DSP RX1 Source", SND_SOC_NOPM, 0, 0, &dsp_rx1_mux), + SND_SOC_DAPM_MUX("DSP RX2 Source", SND_SOC_NOPM, 0, 0, &dsp_rx2_mux), SND_SOC_DAPM_MUX("PCM Source", SND_SOC_NOPM, 0, 0, &pcm_source_mux), SND_SOC_DAPM_SWITCH("DRE", SND_SOC_NOPM, 0, 0, &dre_ctrl), }; static const struct snd_soc_dapm_route cs35l41_audio_map[] = { + {"DSP RX1 Source", "ASPRX1", "ASPRX1"}, + {"DSP RX1 Source", "ASPRX2", "ASPRX2"}, + {"DSP RX2 Source", "ASPRX1", "ASPRX1"}, + {"DSP RX2 Source", "ASPRX2", "ASPRX2"}, + + {"DSP1", NULL, "DSP RX1 Source"}, + {"DSP1", NULL, "DSP RX2 Source"}, + {"ASP TX1 Source", "VMON", "VMON ADC"}, {"ASP TX1 Source", "IMON", "IMON ADC"}, {"ASP TX1 Source", "VPMON", "VPMON ADC"}, {"ASP TX1 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX1 Source", "DSPTX1", "DSP1"}, + {"ASP TX1 Source", "DSPTX2", "DSP1"}, {"ASP TX1 Source", "ASPRX1", "ASPRX1" }, {"ASP TX1 Source", "ASPRX2", "ASPRX2" }, {"ASP TX2 Source", "VMON", "VMON ADC"}, {"ASP TX2 Source", "IMON", "IMON ADC"}, {"ASP TX2 Source", "VPMON", "VPMON ADC"}, {"ASP TX2 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX2 Source", "DSPTX1", "DSP1"}, + {"ASP TX2 Source", "DSPTX2", "DSP1"}, {"ASP TX2 Source", "ASPRX1", "ASPRX1" }, {"ASP TX2 Source", "ASPRX2", "ASPRX2" }, {"ASP TX3 Source", "VMON", "VMON ADC"}, {"ASP TX3 Source", "IMON", "IMON ADC"}, {"ASP TX3 Source", "VPMON", "VPMON ADC"}, {"ASP TX3 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX3 Source", "DSPTX1", "DSP1"}, + {"ASP TX3 Source", "DSPTX2", "DSP1"}, {"ASP TX3 Source", "ASPRX1", "ASPRX1" }, {"ASP TX3 Source", "ASPRX2", "ASPRX2" }, {"ASP TX4 Source", "VMON", "VMON ADC"}, {"ASP TX4 Source", "IMON", "IMON ADC"}, {"ASP TX4 Source", "VPMON", "VPMON ADC"}, {"ASP TX4 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX4 Source", "DSPTX1", "DSP1"}, + {"ASP TX4 Source", "DSPTX2", "DSP1"}, {"ASP TX4 Source", "ASPRX1", "ASPRX1" }, {"ASP TX4 Source", "ASPRX2", "ASPRX2" }, {"ASPTX1", NULL, "ASP TX1 Source"}, @@ -664,12 +711,27 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { {"AMP Capture", NULL, "ASPTX3"}, {"AMP Capture", NULL, "ASPTX4"}, + {"DSP1", NULL, "VMON"}, + {"DSP1", NULL, "IMON"}, + {"DSP1", NULL, "VPMON"}, + {"DSP1", NULL, "VBSTMON"}, + {"DSP1", NULL, "TEMPMON"}, + + {"VMON ADC", NULL, "VMON"}, + {"IMON ADC", NULL, "IMON"}, + {"VPMON ADC", NULL, "VPMON"}, + {"VBSTMON ADC", NULL, "VBSTMON"}, + {"TEMPMON ADC", NULL, "TEMPMON"}, + {"VMON ADC", NULL, "VSENSE"}, {"IMON ADC", NULL, "ISENSE"}, {"VPMON ADC", NULL, "VP"}, {"VBSTMON ADC", NULL, "VBST"}, {"TEMPMON ADC", NULL, "TEMP"}, + {"DSP1 Preload", NULL, "DSP1 Preloader"}, + {"DSP1", NULL, "DSP1 Preloader"}, + {"ASPRX1", NULL, "AMP Playback"}, {"ASPRX2", NULL, "AMP Playback"}, {"DRE", "Switch", "CLASS H"}, @@ -678,39 +740,24 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { {"SPK", NULL, "Main AMP"}, {"PCM Source", "ASP", "ASPRX1"}, + {"PCM Source", "DSP", "DSP1"}, {"CLASS H", NULL, "PCM Source"}, }; -static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, - unsigned int *tx_slot, unsigned int rx_num, - unsigned int *rx_slot) +static const struct cs_dsp_region cs35l41_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = CS35L41_DSP1_PMEM_0 }, + { .type = WMFW_HALO_XM_PACKED, .base = CS35L41_DSP1_XMEM_PACK_0 }, + { .type = WMFW_HALO_YM_PACKED, .base = CS35L41_DSP1_YMEM_PACK_0 }, + {. type = WMFW_ADSP2_XM, .base = CS35L41_DSP1_XMEM_UNPACK24_0}, + {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0}, +}; + +static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n, + unsigned int *tx_slot, unsigned int rx_n, unsigned int *rx_slot) { struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component); - unsigned int val, mask; - int i; - if (tx_num > 4 || rx_num > 2) - return -EINVAL; - - val = 0; - mask = 0; - for (i = 0; i < rx_num; i++) { - dev_dbg(cs35l41->dev, "rx slot %d position = %d\n", i, rx_slot[i]); - val |= rx_slot[i] << (i * 8); - mask |= 0x3F << (i * 8); - } - regmap_update_bits(cs35l41->regmap, CS35L41_SP_FRAME_RX_SLOT, mask, val); - - val = 0; - mask = 0; - for (i = 0; i < tx_num; i++) { - dev_dbg(cs35l41->dev, "tx slot %d position = %d\n", i, tx_slot[i]); - val |= tx_slot[i] << (i * 8); - mask |= 0x3F << (i * 8); - } - regmap_update_bits(cs35l41->regmap, CS35L41_SP_FRAME_TX_SLOT, mask, val); - - return 0; + return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_n, tx_slot, rx_n, rx_slot); } static int cs35l41_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) @@ -946,88 +993,6 @@ static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai, return 0; } -static int cs35l41_boost_config(struct cs35l41_private *cs35l41, - int boost_ind, int boost_cap, int boost_ipk) -{ - unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled; - struct regmap *regmap = cs35l41->regmap; - struct device *dev = cs35l41->dev; - int ret; - - switch (boost_ind) { - case 1000: /* 1.0 uH */ - bst_lbst_val = 0; - break; - case 1200: /* 1.2 uH */ - bst_lbst_val = 1; - break; - case 1500: /* 1.5 uH */ - bst_lbst_val = 2; - break; - case 2200: /* 2.2 uH */ - bst_lbst_val = 3; - break; - default: - dev_err(dev, "Invalid boost inductor value: %d nH\n", boost_ind); - return -EINVAL; - } - - switch (boost_cap) { - case 0 ... 19: - bst_cbst_range = 0; - break; - case 20 ... 50: - bst_cbst_range = 1; - break; - case 51 ... 100: - bst_cbst_range = 2; - break; - case 101 ... 200: - bst_cbst_range = 3; - break; - default: /* 201 uF and greater */ - bst_cbst_range = 4; - } - - ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF, - CS35L41_BST_K1_MASK | CS35L41_BST_K2_MASK, - cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range] - << CS35L41_BST_K1_SHIFT | - cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range] - << CS35L41_BST_K2_SHIFT); - if (ret) { - dev_err(dev, "Failed to write boost coefficients: %d\n", ret); - return ret; - } - - ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST, - CS35L41_BST_SLOPE_MASK | CS35L41_BST_LBST_VAL_MASK, - cs35l41_bst_slope_table[bst_lbst_val] - << CS35L41_BST_SLOPE_SHIFT | - bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT); - if (ret) { - dev_err(dev, "Failed to write boost slope/inductor value: %d\n", ret); - return ret; - } - - if (boost_ipk < 1600 || boost_ipk > 4500) { - dev_err(dev, "Invalid boost inductor peak current: %d mA\n", - boost_ipk); - return -EINVAL; - } - bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10; - - ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR, - CS35L41_BST_IPK_MASK, - bst_ipk_scaled << CS35L41_BST_IPK_SHIFT); - if (ret) { - dev_err(dev, "Failed to write boost inductor peak current: %d\n", ret); - return ret; - } - - return 0; -} - static int cs35l41_set_pdata(struct cs35l41_private *cs35l41) { int ret; @@ -1036,9 +1001,8 @@ static int cs35l41_set_pdata(struct cs35l41_private *cs35l41) /* Required */ if (cs35l41->pdata.bst_ipk && cs35l41->pdata.bst_ind && cs35l41->pdata.bst_cap) { - ret = cs35l41_boost_config(cs35l41, cs35l41->pdata.bst_ind, - cs35l41->pdata.bst_cap, - cs35l41->pdata.bst_ipk); + ret = cs35l41_boost_config(cs35l41->dev, cs35l41->regmap, cs35l41->pdata.bst_ind, + cs35l41->pdata.bst_cap, cs35l41->pdata.bst_ipk); if (ret) { dev_err(cs35l41->dev, "Error in Boost DT config: %d\n", ret); return ret; @@ -1091,6 +1055,20 @@ static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41) return irq_pol; } +static int cs35l41_component_probe(struct snd_soc_component *component) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + + return wm_adsp2_component_probe(&cs35l41->dsp, component); +} + +static void cs35l41_component_remove(struct snd_soc_component *component) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + + wm_adsp2_component_remove(&cs35l41->dsp, component); +} + static const struct snd_soc_dai_ops cs35l41_ops = { .startup = cs35l41_pcm_startup, .set_fmt = cs35l41_set_dai_fmt, @@ -1124,6 +1102,8 @@ static struct snd_soc_dai_driver cs35l41_dai[] = { static const struct snd_soc_component_driver soc_component_dev_cs35l41 = { .name = "cs35l41-codec", + .probe = cs35l41_component_probe, + .remove = cs35l41_component_remove, .dapm_widgets = cs35l41_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets), @@ -1185,50 +1165,90 @@ static int cs35l41_handle_pdata(struct device *dev, return 0; } -static const struct reg_sequence cs35l41_reva0_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, - { 0x00003854, 0x05180240 }, - { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, - { 0x00004310, 0x00000000 }, - { CS35L41_VPVBST_FS_SEL, 0x00000000 }, - { CS35L41_OTP_TRIM_30, 0x9091A1C8 }, - { 0x00003014, 0x0200EE0E }, - { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, - { 0x00000054, 0x00000004 }, - { CS35L41_IRQ1_DB3, 0x00000000 }, - { CS35L41_IRQ2_DB3, 0x00000000 }, - { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, - { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, +static const struct reg_sequence cs35l41_fs_errata_patch[] = { + { CS35L41_DSP1_RX1_RATE, 0x00000001 }, + { CS35L41_DSP1_RX2_RATE, 0x00000001 }, + { CS35L41_DSP1_RX3_RATE, 0x00000001 }, + { CS35L41_DSP1_RX4_RATE, 0x00000001 }, + { CS35L41_DSP1_RX5_RATE, 0x00000001 }, + { CS35L41_DSP1_RX6_RATE, 0x00000001 }, + { CS35L41_DSP1_RX7_RATE, 0x00000001 }, + { CS35L41_DSP1_RX8_RATE, 0x00000001 }, + { CS35L41_DSP1_TX1_RATE, 0x00000001 }, + { CS35L41_DSP1_TX2_RATE, 0x00000001 }, + { CS35L41_DSP1_TX3_RATE, 0x00000001 }, + { CS35L41_DSP1_TX4_RATE, 0x00000001 }, + { CS35L41_DSP1_TX5_RATE, 0x00000001 }, + { CS35L41_DSP1_TX6_RATE, 0x00000001 }, + { CS35L41_DSP1_TX7_RATE, 0x00000001 }, + { CS35L41_DSP1_TX8_RATE, 0x00000001 }, }; -static const struct reg_sequence cs35l41_revb0_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, - { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, - { 0x00004310, 0x00000000 }, - { CS35L41_VPVBST_FS_SEL, 0x00000000 }, - { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, - { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, - { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, -}; +static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) +{ + struct wm_adsp *dsp; + int ret; -static const struct reg_sequence cs35l41_revb2_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, - { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, - { 0x00004310, 0x00000000 }, - { CS35L41_VPVBST_FS_SEL, 0x00000000 }, - { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, - { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, - { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, -}; + dsp = &cs35l41->dsp; + dsp->part = "cs35l41"; + dsp->cs_dsp.num = 1; + dsp->cs_dsp.type = WMFW_HALO; + dsp->cs_dsp.rev = 0; + dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */ + dsp->toggle_preload = true; + dsp->cs_dsp.dev = cs35l41->dev; + dsp->cs_dsp.regmap = cs35l41->regmap; + dsp->cs_dsp.base = CS35L41_DSP1_CTRL_BASE; + dsp->cs_dsp.base_sysinfo = CS35L41_DSP1_SYS_ID; + dsp->cs_dsp.mem = cs35l41_dsp1_regions; + dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l41_dsp1_regions); + dsp->cs_dsp.lock_regions = 0xFFFFFFFF; + + ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_fs_errata_patch, + ARRAY_SIZE(cs35l41_fs_errata_patch)); + if (ret < 0) { + dev_err(cs35l41->dev, "Failed to write fs errata: %d\n", ret); + return ret; + } + + ret = wm_halo_init(dsp); + if (ret) { + dev_err(cs35l41->dev, "wm_halo_init failed: %d\n", ret); + return ret; + } + + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, + CS35L41_INPUT_SRC_VPMON); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_VPMON failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, + CS35L41_INPUT_SRC_CLASSH); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_CLASSH failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC, + CS35L41_INPUT_SRC_TEMPMON); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_TEMPMON failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC, + CS35L41_INPUT_SRC_RSVD); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_RSVD failed: %d\n", ret); + goto err_dsp; + } + + return 0; + +err_dsp: + wm_adsp2_remove(dsp); + + return ret; +} int cs35l41_probe(struct cs35l41_private *cs35l41, struct cs35l41_platform_data *pdata) @@ -1325,39 +1345,20 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } - switch (reg_revid) { - case CS35L41_REVID_A0: - ret = regmap_register_patch(cs35l41->regmap, - cs35l41_reva0_errata_patch, - ARRAY_SIZE(cs35l41_reva0_errata_patch)); - if (ret < 0) { - dev_err(cs35l41->dev, - "Failed to apply A0 errata patch: %d\n", ret); - goto err; - } - break; - case CS35L41_REVID_B0: - ret = regmap_register_patch(cs35l41->regmap, - cs35l41_revb0_errata_patch, - ARRAY_SIZE(cs35l41_revb0_errata_patch)); - if (ret < 0) { - dev_err(cs35l41->dev, - "Failed to apply B0 errata patch: %d\n", ret); - goto err; - } - break; - case CS35L41_REVID_B2: - ret = regmap_register_patch(cs35l41->regmap, - cs35l41_revb2_errata_patch, - ARRAY_SIZE(cs35l41_revb2_errata_patch)); - if (ret < 0) { - dev_err(cs35l41->dev, - "Failed to apply B2 errata patch: %d\n", ret); - goto err; - } - break; + cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); + + ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid); + if (ret) + goto err; + + ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); + if (ret < 0) { + dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); + goto err; } + cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + irq_pol = cs35l41_irq_gpio_config(cs35l41); /* Set interrupt masks for critical errors */ @@ -1367,71 +1368,229 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq, IRQF_ONESHOT | IRQF_SHARED | irq_pol, "cs35l41", cs35l41); - - /* CS35L41 needs INT for PDN_DONE */ if (ret != 0) { dev_err(cs35l41->dev, "Failed to request IRQ: %d\n", ret); goto err; } - ret = cs35l41_otp_unpack(cs35l41); - if (ret < 0) { - dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); - goto err; - } - - ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_CCM_CORE_CTRL, 0); - if (ret < 0) { - dev_err(cs35l41->dev, "Write CCM_CORE_CTRL failed: %d\n", ret); - goto err; - } - - ret = regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, - CS35L41_AMP_EN_MASK, 0); + ret = cs35l41_set_pdata(cs35l41); if (ret < 0) { - dev_err(cs35l41->dev, "Write CS35L41_PWR_CTRL2 failed: %d\n", ret); + dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret); goto err; } - ret = regmap_update_bits(cs35l41->regmap, CS35L41_AMP_GAIN_CTRL, - CS35L41_AMP_GAIN_PCM_MASK, 0); - if (ret < 0) { - dev_err(cs35l41->dev, "Write CS35L41_AMP_GAIN_CTRL failed: %d\n", ret); + ret = cs35l41_dsp_init(cs35l41); + if (ret < 0) goto err; - } - ret = cs35l41_set_pdata(cs35l41); - if (ret < 0) { - dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret); - goto err; - } + pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); + pm_runtime_use_autosuspend(cs35l41->dev); + pm_runtime_mark_last_busy(cs35l41->dev); + pm_runtime_set_active(cs35l41->dev); + pm_runtime_get_noresume(cs35l41->dev); + pm_runtime_enable(cs35l41->dev); ret = devm_snd_soc_register_component(cs35l41->dev, &soc_component_dev_cs35l41, cs35l41_dai, ARRAY_SIZE(cs35l41_dai)); if (ret < 0) { dev_err(cs35l41->dev, "Register codec failed: %d\n", ret); - goto err; + goto err_pm; } + pm_runtime_put_autosuspend(cs35l41->dev); + dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid); return 0; +err_pm: + pm_runtime_disable(cs35l41->dev); + pm_runtime_put_noidle(cs35l41->dev); + + wm_adsp2_remove(&cs35l41->dsp); err: regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); return ret; } +EXPORT_SYMBOL_GPL(cs35l41_probe); void cs35l41_remove(struct cs35l41_private *cs35l41) { + pm_runtime_get_sync(cs35l41->dev); + pm_runtime_disable(cs35l41->dev); + regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); + wm_adsp2_remove(&cs35l41->dsp); + + pm_runtime_put_noidle(cs35l41->dev); + regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); } +EXPORT_SYMBOL_GPL(cs35l41_remove); + +static int __maybe_unused cs35l41_runtime_suspend(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Runtime suspend\n"); + + if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) + return 0; + + dev_dbg(cs35l41->dev, "Enter hibernate\n"); + + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088); + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188); + + // Don't wait for ACK since bus activity would wake the device + regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, + CSPL_MBOX_CMD_HIBERNATE); + + regcache_cache_only(cs35l41->regmap, true); + regcache_mark_dirty(cs35l41->regmap); + + return 0; +} + +static void cs35l41_wait_for_pwrmgt_sts(struct cs35l41_private *cs35l41) +{ + const int pwrmgt_retries = 10; + unsigned int sts; + int i, ret; + + for (i = 0; i < pwrmgt_retries; i++) { + ret = regmap_read(cs35l41->regmap, CS35L41_PWRMGT_STS, &sts); + if (ret) + dev_err(cs35l41->dev, "Failed to read PWRMGT_STS: %d\n", ret); + else if (!(sts & CS35L41_WR_PEND_STS_MASK)) + return; + + udelay(20); + } + + dev_err(cs35l41->dev, "Timed out reading PWRMGT_STS\n"); +} + +static int cs35l41_exit_hibernate(struct cs35l41_private *cs35l41) +{ + const int wake_retries = 20; + const int sleep_retries = 5; + int ret, i, j; + + for (i = 0; i < sleep_retries; i++) { + dev_dbg(cs35l41->dev, "Exit hibernate\n"); + + for (j = 0; j < wake_retries; j++) { + ret = cs35l41_set_cspl_mbox_cmd(cs35l41, + CSPL_MBOX_CMD_OUT_OF_HIBERNATE); + if (!ret) + break; + + usleep_range(100, 200); + } + + if (j < wake_retries) { + dev_dbg(cs35l41->dev, "Wake success at cycle: %d\n", j); + return 0; + } + + dev_err(cs35l41->dev, "Wake failed, re-enter hibernate: %d\n", ret); + + cs35l41_wait_for_pwrmgt_sts(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088); + + cs35l41_wait_for_pwrmgt_sts(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188); + + cs35l41_wait_for_pwrmgt_sts(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_PWRMGT_CTL, 0x3); + } + + dev_err(cs35l41->dev, "Timed out waking device\n"); + + return -ETIMEDOUT; +} + +static int __maybe_unused cs35l41_runtime_resume(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + int ret; + + dev_dbg(cs35l41->dev, "Runtime resume\n"); + + if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) + return 0; + + regcache_cache_only(cs35l41->regmap, false); + + ret = cs35l41_exit_hibernate(cs35l41); + if (ret) + return ret; + + /* Test key needs to be unlocked to allow the OTP settings to re-apply */ + cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); + ret = regcache_sync(cs35l41->regmap); + cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + if (ret) { + dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret); + return ret; + } + + return 0; +} + +static int __maybe_unused cs35l41_sys_suspend(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "System suspend, disabling IRQ\n"); + disable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_suspend_noirq(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Late system suspend, reenabling IRQ\n"); + enable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_resume_noirq(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Early system resume, disabling IRQ\n"); + disable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_resume(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "System resume, reenabling IRQ\n"); + enable_irq(cs35l41->irq); + + return 0; +} + +const struct dev_pm_ops cs35l41_pm_ops = { + SET_RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL) + + SET_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq) +}; +EXPORT_SYMBOL_GPL(cs35l41_pm_ops); MODULE_DESCRIPTION("ASoC CS35L41 driver"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>"); diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h index 48485b08a6f1..88a3d6e3434f 100644 --- a/sound/soc/codecs/cs35l41.h +++ b/sound/soc/codecs/cs35l41.h @@ -11,751 +11,38 @@ #define __CS35L41_H__ #include <linux/gpio/consumer.h> -#include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/firmware.h> #include <sound/core.h> #include <sound/cs35l41.h> -#define CS35L41_FIRSTREG 0x00000000 -#define CS35L41_LASTREG 0x03804FE8 -#define CS35L41_DEVID 0x00000000 -#define CS35L41_REVID 0x00000004 -#define CS35L41_FABID 0x00000008 -#define CS35L41_RELID 0x0000000C -#define CS35L41_OTPID 0x00000010 -#define CS35L41_SFT_RESET 0x00000020 -#define CS35L41_TEST_KEY_CTL 0x00000040 -#define CS35L41_USER_KEY_CTL 0x00000044 -#define CS35L41_OTP_MEM0 0x00000400 -#define CS35L41_OTP_MEM31 0x0000047C -#define CS35L41_OTP_CTRL0 0x00000500 -#define CS35L41_OTP_CTRL1 0x00000504 -#define CS35L41_OTP_CTRL3 0x00000508 -#define CS35L41_OTP_CTRL4 0x0000050C -#define CS35L41_OTP_CTRL5 0x00000510 -#define CS35L41_OTP_CTRL6 0x00000514 -#define CS35L41_OTP_CTRL7 0x00000518 -#define CS35L41_OTP_CTRL8 0x0000051C -#define CS35L41_PWR_CTRL1 0x00002014 -#define CS35L41_PWR_CTRL2 0x00002018 -#define CS35L41_PWR_CTRL3 0x0000201C -#define CS35L41_CTRL_OVRRIDE 0x00002020 -#define CS35L41_AMP_OUT_MUTE 0x00002024 -#define CS35L41_PROTECT_REL_ERR_IGN 0x00002034 -#define CS35L41_GPIO_PAD_CONTROL 0x0000242C -#define CS35L41_JTAG_CONTROL 0x00002438 -#define CS35L41_PLL_CLK_CTRL 0x00002C04 -#define CS35L41_DSP_CLK_CTRL 0x00002C08 -#define CS35L41_GLOBAL_CLK_CTRL 0x00002C0C -#define CS35L41_DATA_FS_SEL 0x00002C10 -#define CS35L41_TST_FS_MON0 0x00002D10 -#define CS35L41_MDSYNC_EN 0x00003400 -#define CS35L41_MDSYNC_TX_ID 0x00003408 -#define CS35L41_MDSYNC_PWR_CTRL 0x0000340C -#define CS35L41_MDSYNC_DATA_TX 0x00003410 -#define CS35L41_MDSYNC_TX_STATUS 0x00003414 -#define CS35L41_MDSYNC_DATA_RX 0x0000341C -#define CS35L41_MDSYNC_RX_STATUS 0x00003420 -#define CS35L41_MDSYNC_ERR_STATUS 0x00003424 -#define CS35L41_MDSYNC_SYNC_PTE2 0x00003528 -#define CS35L41_MDSYNC_SYNC_PTE3 0x0000352C -#define CS35L41_MDSYNC_SYNC_MSM_STATUS 0x0000353C -#define CS35L41_BSTCVRT_VCTRL1 0x00003800 -#define CS35L41_BSTCVRT_VCTRL2 0x00003804 -#define CS35L41_BSTCVRT_PEAK_CUR 0x00003808 -#define CS35L41_BSTCVRT_SFT_RAMP 0x0000380C -#define CS35L41_BSTCVRT_COEFF 0x00003810 -#define CS35L41_BSTCVRT_SLOPE_LBST 0x00003814 -#define CS35L41_BSTCVRT_SW_FREQ 0x00003818 -#define CS35L41_BSTCVRT_DCM_CTRL 0x0000381C -#define CS35L41_BSTCVRT_DCM_MODE_FORCE 0x00003820 -#define CS35L41_BSTCVRT_OVERVOLT_CTRL 0x00003830 -#define CS35L41_VI_VOL_POL 0x00004000 -#define CS35L41_VIMON_SPKMON_RESYNC 0x00004100 -#define CS35L41_DTEMP_WARN_THLD 0x00004220 -#define CS35L41_DTEMP_CFG 0x00004224 -#define CS35L41_DTEMP_EN 0x00004308 -#define CS35L41_VPVBST_FS_SEL 0x00004400 -#define CS35L41_SP_ENABLES 0x00004800 -#define CS35L41_SP_RATE_CTRL 0x00004804 -#define CS35L41_SP_FORMAT 0x00004808 -#define CS35L41_SP_HIZ_CTRL 0x0000480C -#define CS35L41_SP_FRAME_TX_SLOT 0x00004810 -#define CS35L41_SP_FRAME_RX_SLOT 0x00004820 -#define CS35L41_SP_TX_WL 0x00004830 -#define CS35L41_SP_RX_WL 0x00004840 -#define CS35L41_ASP_CONTROL4 0x00004854 -#define CS35L41_DAC_PCM1_SRC 0x00004C00 -#define CS35L41_ASP_TX1_SRC 0x00004C20 -#define CS35L41_ASP_TX2_SRC 0x00004C24 -#define CS35L41_ASP_TX3_SRC 0x00004C28 -#define CS35L41_ASP_TX4_SRC 0x00004C2C -#define CS35L41_DSP1_RX1_SRC 0x00004C40 -#define CS35L41_DSP1_RX2_SRC 0x00004C44 -#define CS35L41_DSP1_RX3_SRC 0x00004C48 -#define CS35L41_DSP1_RX4_SRC 0x00004C4C -#define CS35L41_DSP1_RX5_SRC 0x00004C50 -#define CS35L41_DSP1_RX6_SRC 0x00004C54 -#define CS35L41_DSP1_RX7_SRC 0x00004C58 -#define CS35L41_DSP1_RX8_SRC 0x00004C5C -#define CS35L41_NGATE1_SRC 0x00004C60 -#define CS35L41_NGATE2_SRC 0x00004C64 -#define CS35L41_AMP_DIG_VOL_CTRL 0x00006000 -#define CS35L41_VPBR_CFG 0x00006404 -#define CS35L41_VBBR_CFG 0x00006408 -#define CS35L41_VPBR_STATUS 0x0000640C -#define CS35L41_VBBR_STATUS 0x00006410 -#define CS35L41_OVERTEMP_CFG 0x00006414 -#define CS35L41_AMP_ERR_VOL 0x00006418 -#define CS35L41_VOL_STATUS_TO_DSP 0x00006450 -#define CS35L41_CLASSH_CFG 0x00006800 -#define CS35L41_WKFET_CFG 0x00006804 -#define CS35L41_NG_CFG 0x00006808 -#define CS35L41_AMP_GAIN_CTRL 0x00006C04 -#define CS35L41_DAC_MSM_CFG 0x00007400 -#define CS35L41_IRQ1_CFG 0x00010000 -#define CS35L41_IRQ1_STATUS 0x00010004 -#define CS35L41_IRQ1_STATUS1 0x00010010 -#define CS35L41_IRQ1_STATUS2 0x00010014 -#define CS35L41_IRQ1_STATUS3 0x00010018 -#define CS35L41_IRQ1_STATUS4 0x0001001C -#define CS35L41_IRQ1_RAW_STATUS1 0x00010090 -#define CS35L41_IRQ1_RAW_STATUS2 0x00010094 -#define CS35L41_IRQ1_RAW_STATUS3 0x00010098 -#define CS35L41_IRQ1_RAW_STATUS4 0x0001009C -#define CS35L41_IRQ1_MASK1 0x00010110 -#define CS35L41_IRQ1_MASK2 0x00010114 -#define CS35L41_IRQ1_MASK3 0x00010118 -#define CS35L41_IRQ1_MASK4 0x0001011C -#define CS35L41_IRQ1_FRC1 0x00010190 -#define CS35L41_IRQ1_FRC2 0x00010194 -#define CS35L41_IRQ1_FRC3 0x00010198 -#define CS35L41_IRQ1_FRC4 0x0001019C -#define CS35L41_IRQ1_EDGE1 0x00010210 -#define CS35L41_IRQ1_EDGE4 0x0001021C -#define CS35L41_IRQ1_POL1 0x00010290 -#define CS35L41_IRQ1_POL2 0x00010294 -#define CS35L41_IRQ1_POL3 0x00010298 -#define CS35L41_IRQ1_POL4 0x0001029C -#define CS35L41_IRQ1_DB3 0x00010318 -#define CS35L41_IRQ2_CFG 0x00010800 -#define CS35L41_IRQ2_STATUS 0x00010804 -#define CS35L41_IRQ2_STATUS1 0x00010810 -#define CS35L41_IRQ2_STATUS2 0x00010814 -#define CS35L41_IRQ2_STATUS3 0x00010818 -#define CS35L41_IRQ2_STATUS4 0x0001081C -#define CS35L41_IRQ2_RAW_STATUS1 0x00010890 -#define CS35L41_IRQ2_RAW_STATUS2 0x00010894 -#define CS35L41_IRQ2_RAW_STATUS3 0x00010898 -#define CS35L41_IRQ2_RAW_STATUS4 0x0001089C -#define CS35L41_IRQ2_MASK1 0x00010910 -#define CS35L41_IRQ2_MASK2 0x00010914 -#define CS35L41_IRQ2_MASK3 0x00010918 -#define CS35L41_IRQ2_MASK4 0x0001091C -#define CS35L41_IRQ2_FRC1 0x00010990 -#define CS35L41_IRQ2_FRC2 0x00010994 -#define CS35L41_IRQ2_FRC3 0x00010998 -#define CS35L41_IRQ2_FRC4 0x0001099C -#define CS35L41_IRQ2_EDGE1 0x00010A10 -#define CS35L41_IRQ2_EDGE4 0x00010A1C -#define CS35L41_IRQ2_POL1 0x00010A90 -#define CS35L41_IRQ2_POL2 0x00010A94 -#define CS35L41_IRQ2_POL3 0x00010A98 -#define CS35L41_IRQ2_POL4 0x00010A9C -#define CS35L41_IRQ2_DB3 0x00010B18 -#define CS35L41_GPIO_STATUS1 0x00011000 -#define CS35L41_GPIO1_CTRL1 0x00011008 -#define CS35L41_GPIO2_CTRL1 0x0001100C -#define CS35L41_MIXER_NGATE_CFG 0x00012000 -#define CS35L41_MIXER_NGATE_CH1_CFG 0x00012004 -#define CS35L41_MIXER_NGATE_CH2_CFG 0x00012008 -#define CS35L41_DSP_MBOX_1 0x00013000 -#define CS35L41_DSP_MBOX_2 0x00013004 -#define CS35L41_DSP_MBOX_3 0x00013008 -#define CS35L41_DSP_MBOX_4 0x0001300C -#define CS35L41_DSP_MBOX_5 0x00013010 -#define CS35L41_DSP_MBOX_6 0x00013014 -#define CS35L41_DSP_MBOX_7 0x00013018 -#define CS35L41_DSP_MBOX_8 0x0001301C -#define CS35L41_DSP_VIRT1_MBOX_1 0x00013020 -#define CS35L41_DSP_VIRT1_MBOX_2 0x00013024 -#define CS35L41_DSP_VIRT1_MBOX_3 0x00013028 -#define CS35L41_DSP_VIRT1_MBOX_4 0x0001302C -#define CS35L41_DSP_VIRT1_MBOX_5 0x00013030 -#define CS35L41_DSP_VIRT1_MBOX_6 0x00013034 -#define CS35L41_DSP_VIRT1_MBOX_7 0x00013038 -#define CS35L41_DSP_VIRT1_MBOX_8 0x0001303C -#define CS35L41_DSP_VIRT2_MBOX_1 0x00013040 -#define CS35L41_DSP_VIRT2_MBOX_2 0x00013044 -#define CS35L41_DSP_VIRT2_MBOX_3 0x00013048 -#define CS35L41_DSP_VIRT2_MBOX_4 0x0001304C -#define CS35L41_DSP_VIRT2_MBOX_5 0x00013050 -#define CS35L41_DSP_VIRT2_MBOX_6 0x00013054 -#define CS35L41_DSP_VIRT2_MBOX_7 0x00013058 -#define CS35L41_DSP_VIRT2_MBOX_8 0x0001305C -#define CS35L41_CLOCK_DETECT_1 0x00014000 -#define CS35L41_TIMER1_CONTROL 0x00015000 -#define CS35L41_TIMER1_COUNT_PRESET 0x00015004 -#define CS35L41_TIMER1_START_STOP 0x0001500C -#define CS35L41_TIMER1_STATUS 0x00015010 -#define CS35L41_TIMER1_COUNT_READBACK 0x00015014 -#define CS35L41_TIMER1_DSP_CLK_CFG 0x00015018 -#define CS35L41_TIMER1_DSP_CLK_STATUS 0x0001501C -#define CS35L41_TIMER2_CONTROL 0x00015100 -#define CS35L41_TIMER2_COUNT_PRESET 0x00015104 -#define CS35L41_TIMER2_START_STOP 0x0001510C -#define CS35L41_TIMER2_STATUS 0x00015110 -#define CS35L41_TIMER2_COUNT_READBACK 0x00015114 -#define CS35L41_TIMER2_DSP_CLK_CFG 0x00015118 -#define CS35L41_TIMER2_DSP_CLK_STATUS 0x0001511C -#define CS35L41_DFT_JTAG_CONTROL 0x00016000 -#define CS35L41_DIE_STS1 0x00017040 -#define CS35L41_DIE_STS2 0x00017044 -#define CS35L41_TEMP_CAL1 0x00017048 -#define CS35L41_TEMP_CAL2 0x0001704C -#define CS35L41_DSP1_XMEM_PACK_0 0x02000000 -#define CS35L41_DSP1_XMEM_PACK_3068 0x02002FF0 -#define CS35L41_DSP1_XMEM_UNPACK32_0 0x02400000 -#define CS35L41_DSP1_XMEM_UNPACK32_2046 0x02401FF8 -#define CS35L41_DSP1_TIMESTAMP_COUNT 0x025C0800 -#define CS35L41_DSP1_SYS_ID 0x025E0000 -#define CS35L41_DSP1_SYS_VERSION 0x025E0004 -#define CS35L41_DSP1_SYS_CORE_ID 0x025E0008 -#define CS35L41_DSP1_SYS_AHB_ADDR 0x025E000C -#define CS35L41_DSP1_SYS_XSRAM_SIZE 0x025E0010 -#define CS35L41_DSP1_SYS_YSRAM_SIZE 0x025E0018 -#define CS35L41_DSP1_SYS_PSRAM_SIZE 0x025E0020 -#define CS35L41_DSP1_SYS_PM_BOOT_SIZE 0x025E0028 -#define CS35L41_DSP1_SYS_FEATURES 0x025E002C -#define CS35L41_DSP1_SYS_FIR_FILTERS 0x025E0030 -#define CS35L41_DSP1_SYS_LMS_FILTERS 0x025E0034 -#define CS35L41_DSP1_SYS_XM_BANK_SIZE 0x025E0038 -#define CS35L41_DSP1_SYS_YM_BANK_SIZE 0x025E003C -#define CS35L41_DSP1_SYS_PM_BANK_SIZE 0x025E0040 -#define CS35L41_DSP1_AHBM_WIN0_CTRL0 0x025E2000 -#define CS35L41_DSP1_AHBM_WIN0_CTRL1 0x025E2004 -#define CS35L41_DSP1_AHBM_WIN1_CTRL0 0x025E2008 -#define CS35L41_DSP1_AHBM_WIN1_CTRL1 0x025E200C -#define CS35L41_DSP1_AHBM_WIN2_CTRL0 0x025E2010 -#define CS35L41_DSP1_AHBM_WIN2_CTRL1 0x025E2014 -#define CS35L41_DSP1_AHBM_WIN3_CTRL0 0x025E2018 -#define CS35L41_DSP1_AHBM_WIN3_CTRL1 0x025E201C -#define CS35L41_DSP1_AHBM_WIN4_CTRL0 0x025E2020 -#define CS35L41_DSP1_AHBM_WIN4_CTRL1 0x025E2024 -#define CS35L41_DSP1_AHBM_WIN5_CTRL0 0x025E2028 -#define CS35L41_DSP1_AHBM_WIN5_CTRL1 0x025E202C -#define CS35L41_DSP1_AHBM_WIN6_CTRL0 0x025E2030 -#define CS35L41_DSP1_AHBM_WIN6_CTRL1 0x025E2034 -#define CS35L41_DSP1_AHBM_WIN7_CTRL0 0x025E2038 -#define CS35L41_DSP1_AHBM_WIN7_CTRL1 0x025E203C -#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL0 0x025E2040 -#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL1 0x025E2044 -#define CS35L41_DSP1_XMEM_UNPACK24_0 0x02800000 -#define CS35L41_DSP1_XMEM_UNPACK24_4093 0x02803FF4 -#define CS35L41_DSP1_CTRL_BASE 0x02B80000 -#define CS35L41_DSP1_CORE_SOFT_RESET 0x02B80010 -#define CS35L41_DSP1_DEBUG 0x02B80040 -#define CS35L41_DSP1_TIMER_CTRL 0x02B80048 -#define CS35L41_DSP1_STREAM_ARB_CTRL 0x02B80050 -#define CS35L41_DSP1_RX1_RATE 0x02B80080 -#define CS35L41_DSP1_RX2_RATE 0x02B80088 -#define CS35L41_DSP1_RX3_RATE 0x02B80090 -#define CS35L41_DSP1_RX4_RATE 0x02B80098 -#define CS35L41_DSP1_RX5_RATE 0x02B800A0 -#define CS35L41_DSP1_RX6_RATE 0x02B800A8 -#define CS35L41_DSP1_RX7_RATE 0x02B800B0 -#define CS35L41_DSP1_RX8_RATE 0x02B800B8 -#define CS35L41_DSP1_TX1_RATE 0x02B80280 -#define CS35L41_DSP1_TX2_RATE 0x02B80288 -#define CS35L41_DSP1_TX3_RATE 0x02B80290 -#define CS35L41_DSP1_TX4_RATE 0x02B80298 -#define CS35L41_DSP1_TX5_RATE 0x02B802A0 -#define CS35L41_DSP1_TX6_RATE 0x02B802A8 -#define CS35L41_DSP1_TX7_RATE 0x02B802B0 -#define CS35L41_DSP1_TX8_RATE 0x02B802B8 -#define CS35L41_DSP1_NMI_CTRL1 0x02B80480 -#define CS35L41_DSP1_NMI_CTRL2 0x02B80488 -#define CS35L41_DSP1_NMI_CTRL3 0x02B80490 -#define CS35L41_DSP1_NMI_CTRL4 0x02B80498 -#define CS35L41_DSP1_NMI_CTRL5 0x02B804A0 -#define CS35L41_DSP1_NMI_CTRL6 0x02B804A8 -#define CS35L41_DSP1_NMI_CTRL7 0x02B804B0 -#define CS35L41_DSP1_NMI_CTRL8 0x02B804B8 -#define CS35L41_DSP1_RESUME_CTRL 0x02B80500 -#define CS35L41_DSP1_IRQ1_CTRL 0x02B80508 -#define CS35L41_DSP1_IRQ2_CTRL 0x02B80510 -#define CS35L41_DSP1_IRQ3_CTRL 0x02B80518 -#define CS35L41_DSP1_IRQ4_CTRL 0x02B80520 -#define CS35L41_DSP1_IRQ5_CTRL 0x02B80528 -#define CS35L41_DSP1_IRQ6_CTRL 0x02B80530 -#define CS35L41_DSP1_IRQ7_CTRL 0x02B80538 -#define CS35L41_DSP1_IRQ8_CTRL 0x02B80540 -#define CS35L41_DSP1_IRQ9_CTRL 0x02B80548 -#define CS35L41_DSP1_IRQ10_CTRL 0x02B80550 -#define CS35L41_DSP1_IRQ11_CTRL 0x02B80558 -#define CS35L41_DSP1_IRQ12_CTRL 0x02B80560 -#define CS35L41_DSP1_IRQ13_CTRL 0x02B80568 -#define CS35L41_DSP1_IRQ14_CTRL 0x02B80570 -#define CS35L41_DSP1_IRQ15_CTRL 0x02B80578 -#define CS35L41_DSP1_IRQ16_CTRL 0x02B80580 -#define CS35L41_DSP1_IRQ17_CTRL 0x02B80588 -#define CS35L41_DSP1_IRQ18_CTRL 0x02B80590 -#define CS35L41_DSP1_IRQ19_CTRL 0x02B80598 -#define CS35L41_DSP1_IRQ20_CTRL 0x02B805A0 -#define CS35L41_DSP1_IRQ21_CTRL 0x02B805A8 -#define CS35L41_DSP1_IRQ22_CTRL 0x02B805B0 -#define CS35L41_DSP1_IRQ23_CTRL 0x02B805B8 -#define CS35L41_DSP1_SCRATCH1 0x02B805C0 -#define CS35L41_DSP1_SCRATCH2 0x02B805C8 -#define CS35L41_DSP1_SCRATCH3 0x02B805D0 -#define CS35L41_DSP1_SCRATCH4 0x02B805D8 -#define CS35L41_DSP1_CCM_CORE_CTRL 0x02BC1000 -#define CS35L41_DSP1_CCM_CLK_OVERRIDE 0x02BC1008 -#define CS35L41_DSP1_XM_MSTR_EN 0x02BC2000 -#define CS35L41_DSP1_XM_CORE_PRI 0x02BC2008 -#define CS35L41_DSP1_XM_AHB_PACK_PL_PRI 0x02BC2010 -#define CS35L41_DSP1_XM_AHB_UP_PL_PRI 0x02BC2018 -#define CS35L41_DSP1_XM_ACCEL_PL0_PRI 0x02BC2020 -#define CS35L41_DSP1_XM_NPL0_PRI 0x02BC2078 -#define CS35L41_DSP1_YM_MSTR_EN 0x02BC20C0 -#define CS35L41_DSP1_YM_CORE_PRI 0x02BC20C8 -#define CS35L41_DSP1_YM_AHB_PACK_PL_PRI 0x02BC20D0 -#define CS35L41_DSP1_YM_AHB_UP_PL_PRI 0x02BC20D8 -#define CS35L41_DSP1_YM_ACCEL_PL0_PRI 0x02BC20E0 -#define CS35L41_DSP1_YM_NPL0_PRI 0x02BC2138 -#define CS35L41_DSP1_PM_MSTR_EN 0x02BC2180 -#define CS35L41_DSP1_PM_PATCH0_ADDR 0x02BC2188 -#define CS35L41_DSP1_PM_PATCH0_EN 0x02BC218C -#define CS35L41_DSP1_PM_PATCH0_DATA_LO 0x02BC2190 -#define CS35L41_DSP1_PM_PATCH0_DATA_HI 0x02BC2194 -#define CS35L41_DSP1_PM_PATCH1_ADDR 0x02BC2198 -#define CS35L41_DSP1_PM_PATCH1_EN 0x02BC219C -#define CS35L41_DSP1_PM_PATCH1_DATA_LO 0x02BC21A0 -#define CS35L41_DSP1_PM_PATCH1_DATA_HI 0x02BC21A4 -#define CS35L41_DSP1_PM_PATCH2_ADDR 0x02BC21A8 -#define CS35L41_DSP1_PM_PATCH2_EN 0x02BC21AC -#define CS35L41_DSP1_PM_PATCH2_DATA_LO 0x02BC21B0 -#define CS35L41_DSP1_PM_PATCH2_DATA_HI 0x02BC21B4 -#define CS35L41_DSP1_PM_PATCH3_ADDR 0x02BC21B8 -#define CS35L41_DSP1_PM_PATCH3_EN 0x02BC21BC -#define CS35L41_DSP1_PM_PATCH3_DATA_LO 0x02BC21C0 -#define CS35L41_DSP1_PM_PATCH3_DATA_HI 0x02BC21C4 -#define CS35L41_DSP1_PM_PATCH4_ADDR 0x02BC21C8 -#define CS35L41_DSP1_PM_PATCH4_EN 0x02BC21CC -#define CS35L41_DSP1_PM_PATCH4_DATA_LO 0x02BC21D0 -#define CS35L41_DSP1_PM_PATCH4_DATA_HI 0x02BC21D4 -#define CS35L41_DSP1_PM_PATCH5_ADDR 0x02BC21D8 -#define CS35L41_DSP1_PM_PATCH5_EN 0x02BC21DC -#define CS35L41_DSP1_PM_PATCH5_DATA_LO 0x02BC21E0 -#define CS35L41_DSP1_PM_PATCH5_DATA_HI 0x02BC21E4 -#define CS35L41_DSP1_PM_PATCH6_ADDR 0x02BC21E8 -#define CS35L41_DSP1_PM_PATCH6_EN 0x02BC21EC -#define CS35L41_DSP1_PM_PATCH6_DATA_LO 0x02BC21F0 -#define CS35L41_DSP1_PM_PATCH6_DATA_HI 0x02BC21F4 -#define CS35L41_DSP1_PM_PATCH7_ADDR 0x02BC21F8 -#define CS35L41_DSP1_PM_PATCH7_EN 0x02BC21FC -#define CS35L41_DSP1_PM_PATCH7_DATA_LO 0x02BC2200 -#define CS35L41_DSP1_PM_PATCH7_DATA_HI 0x02BC2204 -#define CS35L41_DSP1_MPU_XM_ACCESS0 0x02BC3000 -#define CS35L41_DSP1_MPU_YM_ACCESS0 0x02BC3004 -#define CS35L41_DSP1_MPU_WNDW_ACCESS0 0x02BC3008 -#define CS35L41_DSP1_MPU_XREG_ACCESS0 0x02BC300C -#define CS35L41_DSP1_MPU_YREG_ACCESS0 0x02BC3014 -#define CS35L41_DSP1_MPU_XM_ACCESS1 0x02BC3018 -#define CS35L41_DSP1_MPU_YM_ACCESS1 0x02BC301C -#define CS35L41_DSP1_MPU_WNDW_ACCESS1 0x02BC3020 -#define CS35L41_DSP1_MPU_XREG_ACCESS1 0x02BC3024 -#define CS35L41_DSP1_MPU_YREG_ACCESS1 0x02BC302C -#define CS35L41_DSP1_MPU_XM_ACCESS2 0x02BC3030 -#define CS35L41_DSP1_MPU_YM_ACCESS2 0x02BC3034 -#define CS35L41_DSP1_MPU_WNDW_ACCESS2 0x02BC3038 -#define CS35L41_DSP1_MPU_XREG_ACCESS2 0x02BC303C -#define CS35L41_DSP1_MPU_YREG_ACCESS2 0x02BC3044 -#define CS35L41_DSP1_MPU_XM_ACCESS3 0x02BC3048 -#define CS35L41_DSP1_MPU_YM_ACCESS3 0x02BC304C -#define CS35L41_DSP1_MPU_WNDW_ACCESS3 0x02BC3050 -#define CS35L41_DSP1_MPU_XREG_ACCESS3 0x02BC3054 -#define CS35L41_DSP1_MPU_YREG_ACCESS3 0x02BC305C -#define CS35L41_DSP1_MPU_XM_VIO_ADDR 0x02BC3100 -#define CS35L41_DSP1_MPU_XM_VIO_STATUS 0x02BC3104 -#define CS35L41_DSP1_MPU_YM_VIO_ADDR 0x02BC3108 -#define CS35L41_DSP1_MPU_YM_VIO_STATUS 0x02BC310C -#define CS35L41_DSP1_MPU_PM_VIO_ADDR 0x02BC3110 -#define CS35L41_DSP1_MPU_PM_VIO_STATUS 0x02BC3114 -#define CS35L41_DSP1_MPU_LOCK_CONFIG 0x02BC3140 -#define CS35L41_DSP1_MPU_WDT_RST_CTRL 0x02BC3180 -#define CS35L41_DSP1_STRMARB_MSTR0_CFG0 0x02BC5000 -#define CS35L41_DSP1_STRMARB_MSTR0_CFG1 0x02BC5004 -#define CS35L41_DSP1_STRMARB_MSTR0_CFG2 0x02BC5008 -#define CS35L41_DSP1_STRMARB_MSTR1_CFG0 0x02BC5010 -#define CS35L41_DSP1_STRMARB_MSTR1_CFG1 0x02BC5014 -#define CS35L41_DSP1_STRMARB_MSTR1_CFG2 0x02BC5018 -#define CS35L41_DSP1_STRMARB_MSTR2_CFG0 0x02BC5020 -#define CS35L41_DSP1_STRMARB_MSTR2_CFG1 0x02BC5024 -#define CS35L41_DSP1_STRMARB_MSTR2_CFG2 0x02BC5028 -#define CS35L41_DSP1_STRMARB_MSTR3_CFG0 0x02BC5030 -#define CS35L41_DSP1_STRMARB_MSTR3_CFG1 0x02BC5034 -#define CS35L41_DSP1_STRMARB_MSTR3_CFG2 0x02BC5038 -#define CS35L41_DSP1_STRMARB_MSTR4_CFG0 0x02BC5040 -#define CS35L41_DSP1_STRMARB_MSTR4_CFG1 0x02BC5044 -#define CS35L41_DSP1_STRMARB_MSTR4_CFG2 0x02BC5048 -#define CS35L41_DSP1_STRMARB_MSTR5_CFG0 0x02BC5050 -#define CS35L41_DSP1_STRMARB_MSTR5_CFG1 0x02BC5054 -#define CS35L41_DSP1_STRMARB_MSTR5_CFG2 0x02BC5058 -#define CS35L41_DSP1_STRMARB_MSTR6_CFG0 0x02BC5060 -#define CS35L41_DSP1_STRMARB_MSTR6_CFG1 0x02BC5064 -#define CS35L41_DSP1_STRMARB_MSTR6_CFG2 0x02BC5068 -#define CS35L41_DSP1_STRMARB_MSTR7_CFG0 0x02BC5070 -#define CS35L41_DSP1_STRMARB_MSTR7_CFG1 0x02BC5074 -#define CS35L41_DSP1_STRMARB_MSTR7_CFG2 0x02BC5078 -#define CS35L41_DSP1_STRMARB_TX0_CFG0 0x02BC5200 -#define CS35L41_DSP1_STRMARB_TX0_CFG1 0x02BC5204 -#define CS35L41_DSP1_STRMARB_TX1_CFG0 0x02BC5208 -#define CS35L41_DSP1_STRMARB_TX1_CFG1 0x02BC520C -#define CS35L41_DSP1_STRMARB_TX2_CFG0 0x02BC5210 -#define CS35L41_DSP1_STRMARB_TX2_CFG1 0x02BC5214 -#define CS35L41_DSP1_STRMARB_TX3_CFG0 0x02BC5218 -#define CS35L41_DSP1_STRMARB_TX3_CFG1 0x02BC521C -#define CS35L41_DSP1_STRMARB_TX4_CFG0 0x02BC5220 -#define CS35L41_DSP1_STRMARB_TX4_CFG1 0x02BC5224 -#define CS35L41_DSP1_STRMARB_TX5_CFG0 0x02BC5228 -#define CS35L41_DSP1_STRMARB_TX5_CFG1 0x02BC522C -#define CS35L41_DSP1_STRMARB_TX6_CFG0 0x02BC5230 -#define CS35L41_DSP1_STRMARB_TX6_CFG1 0x02BC5234 -#define CS35L41_DSP1_STRMARB_TX7_CFG0 0x02BC5238 -#define CS35L41_DSP1_STRMARB_TX7_CFG1 0x02BC523C -#define CS35L41_DSP1_STRMARB_RX0_CFG0 0x02BC5400 -#define CS35L41_DSP1_STRMARB_RX0_CFG1 0x02BC5404 -#define CS35L41_DSP1_STRMARB_RX1_CFG0 0x02BC5408 -#define CS35L41_DSP1_STRMARB_RX1_CFG1 0x02BC540C -#define CS35L41_DSP1_STRMARB_RX2_CFG0 0x02BC5410 -#define CS35L41_DSP1_STRMARB_RX2_CFG1 0x02BC5414 -#define CS35L41_DSP1_STRMARB_RX3_CFG0 0x02BC5418 -#define CS35L41_DSP1_STRMARB_RX3_CFG1 0x02BC541C -#define CS35L41_DSP1_STRMARB_RX4_CFG0 0x02BC5420 -#define CS35L41_DSP1_STRMARB_RX4_CFG1 0x02BC5424 -#define CS35L41_DSP1_STRMARB_RX5_CFG0 0x02BC5428 -#define CS35L41_DSP1_STRMARB_RX5_CFG1 0x02BC542C -#define CS35L41_DSP1_STRMARB_RX6_CFG0 0x02BC5430 -#define CS35L41_DSP1_STRMARB_RX6_CFG1 0x02BC5434 -#define CS35L41_DSP1_STRMARB_RX7_CFG0 0x02BC5438 -#define CS35L41_DSP1_STRMARB_RX7_CFG1 0x02BC543C -#define CS35L41_DSP1_STRMARB_IRQ0_CFG0 0x02BC5600 -#define CS35L41_DSP1_STRMARB_IRQ0_CFG1 0x02BC5604 -#define CS35L41_DSP1_STRMARB_IRQ0_CFG2 0x02BC5608 -#define CS35L41_DSP1_STRMARB_IRQ1_CFG0 0x02BC5610 -#define CS35L41_DSP1_STRMARB_IRQ1_CFG1 0x02BC5614 -#define CS35L41_DSP1_STRMARB_IRQ1_CFG2 0x02BC5618 -#define CS35L41_DSP1_STRMARB_IRQ2_CFG0 0x02BC5620 -#define CS35L41_DSP1_STRMARB_IRQ2_CFG1 0x02BC5624 -#define CS35L41_DSP1_STRMARB_IRQ2_CFG2 0x02BC5628 -#define CS35L41_DSP1_STRMARB_IRQ3_CFG0 0x02BC5630 -#define CS35L41_DSP1_STRMARB_IRQ3_CFG1 0x02BC5634 -#define CS35L41_DSP1_STRMARB_IRQ3_CFG2 0x02BC5638 -#define CS35L41_DSP1_STRMARB_IRQ4_CFG0 0x02BC5640 -#define CS35L41_DSP1_STRMARB_IRQ4_CFG1 0x02BC5644 -#define CS35L41_DSP1_STRMARB_IRQ4_CFG2 0x02BC5648 -#define CS35L41_DSP1_STRMARB_IRQ5_CFG0 0x02BC5650 -#define CS35L41_DSP1_STRMARB_IRQ5_CFG1 0x02BC5654 -#define CS35L41_DSP1_STRMARB_IRQ5_CFG2 0x02BC5658 -#define CS35L41_DSP1_STRMARB_IRQ6_CFG0 0x02BC5660 -#define CS35L41_DSP1_STRMARB_IRQ6_CFG1 0x02BC5664 -#define CS35L41_DSP1_STRMARB_IRQ6_CFG2 0x02BC5668 -#define CS35L41_DSP1_STRMARB_IRQ7_CFG0 0x02BC5670 -#define CS35L41_DSP1_STRMARB_IRQ7_CFG1 0x02BC5674 -#define CS35L41_DSP1_STRMARB_IRQ7_CFG2 0x02BC5678 -#define CS35L41_DSP1_STRMARB_RESYNC_MSK 0x02BC5A00 -#define CS35L41_DSP1_STRMARB_ERR_STATUS 0x02BC5A08 -#define CS35L41_DSP1_INTPCTL_RES_STATIC 0x02BC6000 -#define CS35L41_DSP1_INTPCTL_RES_DYN 0x02BC6004 -#define CS35L41_DSP1_INTPCTL_NMI_CTRL 0x02BC6008 -#define CS35L41_DSP1_INTPCTL_IRQ_INV 0x02BC6010 -#define CS35L41_DSP1_INTPCTL_IRQ_MODE 0x02BC6014 -#define CS35L41_DSP1_INTPCTL_IRQ_EN 0x02BC6018 -#define CS35L41_DSP1_INTPCTL_IRQ_MSK 0x02BC601C -#define CS35L41_DSP1_INTPCTL_IRQ_FLUSH 0x02BC6020 -#define CS35L41_DSP1_INTPCTL_IRQ_MSKCLR 0x02BC6024 -#define CS35L41_DSP1_INTPCTL_IRQ_FRC 0x02BC6028 -#define CS35L41_DSP1_INTPCTL_IRQ_MSKSET 0x02BC602C -#define CS35L41_DSP1_INTPCTL_IRQ_ERR 0x02BC6030 -#define CS35L41_DSP1_INTPCTL_IRQ_PEND 0x02BC6034 -#define CS35L41_DSP1_INTPCTL_IRQ_GEN 0x02BC6038 -#define CS35L41_DSP1_INTPCTL_TESTBITS 0x02BC6040 -#define CS35L41_DSP1_WDT_CONTROL 0x02BC7000 -#define CS35L41_DSP1_WDT_STATUS 0x02BC7008 -#define CS35L41_DSP1_YMEM_PACK_0 0x02C00000 -#define CS35L41_DSP1_YMEM_PACK_1532 0x02C017F0 -#define CS35L41_DSP1_YMEM_UNPACK32_0 0x03000000 -#define CS35L41_DSP1_YMEM_UNPACK32_1022 0x03000FF8 -#define CS35L41_DSP1_YMEM_UNPACK24_0 0x03400000 -#define CS35L41_DSP1_YMEM_UNPACK24_2045 0x03401FF4 -#define CS35L41_DSP1_PMEM_0 0x03800000 -#define CS35L41_DSP1_PMEM_5114 0x03804FE8 - -/*test regs for emulation bringup*/ -#define CS35L41_PLL_OVR 0x00003018 -#define CS35L41_BST_TEST_DUTY 0x00003900 -#define CS35L41_DIGPWM_IOCTRL 0x0000706C - -/*registers populated by OTP*/ -#define CS35L41_OTP_TRIM_1 0x0000208c -#define CS35L41_OTP_TRIM_2 0x00002090 -#define CS35L41_OTP_TRIM_3 0x00003010 -#define CS35L41_OTP_TRIM_4 0x0000300C -#define CS35L41_OTP_TRIM_5 0x0000394C -#define CS35L41_OTP_TRIM_6 0x00003950 -#define CS35L41_OTP_TRIM_7 0x00003954 -#define CS35L41_OTP_TRIM_8 0x00003958 -#define CS35L41_OTP_TRIM_9 0x0000395C -#define CS35L41_OTP_TRIM_10 0x0000416C -#define CS35L41_OTP_TRIM_11 0x00004160 -#define CS35L41_OTP_TRIM_12 0x00004170 -#define CS35L41_OTP_TRIM_13 0x00004360 -#define CS35L41_OTP_TRIM_14 0x00004448 -#define CS35L41_OTP_TRIM_15 0x0000444C -#define CS35L41_OTP_TRIM_16 0x00006E30 -#define CS35L41_OTP_TRIM_17 0x00006E34 -#define CS35L41_OTP_TRIM_18 0x00006E38 -#define CS35L41_OTP_TRIM_19 0x00006E3C -#define CS35L41_OTP_TRIM_20 0x00006E40 -#define CS35L41_OTP_TRIM_21 0x00006E44 -#define CS35L41_OTP_TRIM_22 0x00006E48 -#define CS35L41_OTP_TRIM_23 0x00006E4C -#define CS35L41_OTP_TRIM_24 0x00006E50 -#define CS35L41_OTP_TRIM_25 0x00006E54 -#define CS35L41_OTP_TRIM_26 0x00006E58 -#define CS35L41_OTP_TRIM_27 0x00006E5C -#define CS35L41_OTP_TRIM_28 0x00006E60 -#define CS35L41_OTP_TRIM_29 0x00006E64 -#define CS35L41_OTP_TRIM_30 0x00007418 -#define CS35L41_OTP_TRIM_31 0x0000741C -#define CS35L41_OTP_TRIM_32 0x00007434 -#define CS35L41_OTP_TRIM_33 0x00007068 -#define CS35L41_OTP_TRIM_34 0x0000410C -#define CS35L41_OTP_TRIM_35 0x0000400C -#define CS35L41_OTP_TRIM_36 0x00002030 - -#define CS35L41_MAX_CACHE_REG 36 -#define CS35L41_OTP_SIZE_WORDS 32 -#define CS35L41_NUM_OTP_ELEM 100 -#define CS35L41_NUM_OTP_MAPS 5 - -#define CS35L41_VALID_PDATA 0x80000000 -#define CS35L41_NUM_SUPPLIES 2 - -#define CS35L41_SCLK_MSTR_MASK 0x10 -#define CS35L41_SCLK_MSTR_SHIFT 4 -#define CS35L41_LRCLK_MSTR_MASK 0x01 -#define CS35L41_LRCLK_MSTR_SHIFT 0 -#define CS35L41_SCLK_INV_MASK 0x40 -#define CS35L41_SCLK_INV_SHIFT 6 -#define CS35L41_LRCLK_INV_MASK 0x04 -#define CS35L41_LRCLK_INV_SHIFT 2 -#define CS35L41_SCLK_FRC_MASK 0x20 -#define CS35L41_SCLK_FRC_SHIFT 5 -#define CS35L41_LRCLK_FRC_MASK 0x02 -#define CS35L41_LRCLK_FRC_SHIFT 1 - -#define CS35L41_AMP_GAIN_PCM_MASK 0x3E0 -#define CS35L41_AMP_GAIN_ZC_MASK 0x0400 -#define CS35L41_AMP_GAIN_ZC_SHIFT 10 - -#define CS35L41_BST_CTL_MASK 0xFF -#define CS35L41_BST_CTL_SEL_MASK 0x03 -#define CS35L41_BST_CTL_SEL_REG 0x00 -#define CS35L41_BST_CTL_SEL_CLASSH 0x01 -#define CS35L41_BST_IPK_MASK 0x7F -#define CS35L41_BST_IPK_SHIFT 0 -#define CS35L41_BST_LIM_MASK 0x4 -#define CS35L41_BST_LIM_SHIFT 2 -#define CS35L41_BST_K1_MASK 0x000000FF -#define CS35L41_BST_K1_SHIFT 0 -#define CS35L41_BST_K2_MASK 0x0000FF00 -#define CS35L41_BST_K2_SHIFT 8 -#define CS35L41_BST_SLOPE_MASK 0x0000FF00 -#define CS35L41_BST_SLOPE_SHIFT 8 -#define CS35L41_BST_LBST_VAL_MASK 0x00000003 -#define CS35L41_BST_LBST_VAL_SHIFT 0 - -#define CS35L41_TEMP_THLD_MASK 0x03 -#define CS35L41_VMON_IMON_VOL_MASK 0x07FF07FF -#define CS35L41_PDM_MODE_MASK 0x01 -#define CS35L41_PDM_MODE_SHIFT 0 - -#define CS35L41_CH_MEM_DEPTH_MASK 0x07 -#define CS35L41_CH_MEM_DEPTH_SHIFT 0 -#define CS35L41_CH_HDRM_CTL_MASK 0x007F0000 -#define CS35L41_CH_HDRM_CTL_SHIFT 16 -#define CS35L41_CH_REL_RATE_MASK 0xFF00 -#define CS35L41_CH_REL_RATE_SHIFT 8 -#define CS35L41_CH_WKFET_DLY_MASK 0x001C -#define CS35L41_CH_WKFET_DLY_SHIFT 2 -#define CS35L41_CH_WKFET_THLD_MASK 0x0F00 -#define CS35L41_CH_WKFET_THLD_SHIFT 8 - -#define CS35L41_HW_NG_SEL_MASK 0x3F00 -#define CS35L41_HW_NG_SEL_SHIFT 8 -#define CS35L41_HW_NG_DLY_MASK 0x0070 -#define CS35L41_HW_NG_DLY_SHIFT 4 -#define CS35L41_HW_NG_THLD_MASK 0x0007 -#define CS35L41_HW_NG_THLD_SHIFT 0 - -#define CS35L41_DSP_NG_ENABLE_MASK 0x00010000 -#define CS35L41_DSP_NG_ENABLE_SHIFT 16 -#define CS35L41_DSP_NG_THLD_MASK 0x7 -#define CS35L41_DSP_NG_THLD_SHIFT 0 -#define CS35L41_DSP_NG_DELAY_MASK 0x0F00 -#define CS35L41_DSP_NG_DELAY_SHIFT 8 - -#define CS35L41_ASP_FMT_MASK 0x0700 -#define CS35L41_ASP_FMT_SHIFT 8 -#define CS35L41_ASP_DOUT_HIZ_MASK 0x03 -#define CS35L41_ASP_DOUT_HIZ_SHIFT 0 -#define CS35L41_ASP_WIDTH_16 0x10 -#define CS35L41_ASP_WIDTH_24 0x18 -#define CS35L41_ASP_WIDTH_32 0x20 -#define CS35L41_ASP_WIDTH_TX_MASK 0xFF0000 -#define CS35L41_ASP_WIDTH_TX_SHIFT 16 -#define CS35L41_ASP_WIDTH_RX_MASK 0xFF000000 -#define CS35L41_ASP_WIDTH_RX_SHIFT 24 -#define CS35L41_ASP_RX1_SLOT_MASK 0x3F -#define CS35L41_ASP_RX1_SLOT_SHIFT 0 -#define CS35L41_ASP_RX2_SLOT_MASK 0x3F00 -#define CS35L41_ASP_RX2_SLOT_SHIFT 8 -#define CS35L41_ASP_RX_WL_MASK 0x3F -#define CS35L41_ASP_TX_WL_MASK 0x3F -#define CS35L41_ASP_RX_WL_SHIFT 0 -#define CS35L41_ASP_TX_WL_SHIFT 0 -#define CS35L41_ASP_SOURCE_MASK 0x7F - -#define CS35L41_INPUT_SRC_ASPRX1 0x08 -#define CS35L41_INPUT_SRC_ASPRX2 0x09 -#define CS35L41_INPUT_SRC_VMON 0x18 -#define CS35L41_INPUT_SRC_IMON 0x19 -#define CS35L41_INPUT_SRC_CLASSH 0x21 -#define CS35L41_INPUT_SRC_VPMON 0x28 -#define CS35L41_INPUT_SRC_VBSTMON 0x29 -#define CS35L41_INPUT_SRC_TEMPMON 0x3A -#define CS35L41_INPUT_SRC_RSVD 0x3B -#define CS35L41_INPUT_DSP_TX1 0x32 -#define CS35L41_INPUT_DSP_TX2 0x33 - -#define CS35L41_PLL_CLK_SEL_MASK 0x07 -#define CS35L41_PLL_CLK_SEL_SHIFT 0 -#define CS35L41_PLL_CLK_EN_MASK 0x10 -#define CS35L41_PLL_CLK_EN_SHIFT 4 -#define CS35L41_PLL_OPENLOOP_MASK 0x0800 -#define CS35L41_PLL_OPENLOOP_SHIFT 11 -#define CS35L41_PLLSRC_SCLK 0 -#define CS35L41_PLLSRC_LRCLK 1 -#define CS35L41_PLLSRC_SELF 3 -#define CS35L41_PLLSRC_PDMCLK 4 -#define CS35L41_PLLSRC_MCLK 5 -#define CS35L41_PLLSRC_SWIRE 7 -#define CS35L41_REFCLK_FREQ_MASK 0x7E0 -#define CS35L41_REFCLK_FREQ_SHIFT 5 - -#define CS35L41_GLOBAL_FS_MASK 0x1F -#define CS35L41_GLOBAL_FS_SHIFT 0 - -#define CS35L41_GLOBAL_EN_MASK 0x01 -#define CS35L41_GLOBAL_EN_SHIFT 0 -#define CS35L41_BST_EN_MASK 0x0030 -#define CS35L41_BST_EN_SHIFT 4 -#define CS35L41_BST_EN_DEFAULT 0x2 -#define CS35L41_AMP_EN_SHIFT 0 -#define CS35L41_AMP_EN_MASK 1 - -#define CS35L41_PDN_DONE_MASK 0x00800000 -#define CS35L41_PDN_DONE_SHIFT 23 -#define CS35L41_PUP_DONE_MASK 0x01000000 -#define CS35L41_PUP_DONE_SHIFT 24 - -#define CS35L36_PUP_DONE_IRQ_UNMASK 0x5F -#define CS35L36_PUP_DONE_IRQ_MASK 0xBF - -#define CS35L41_AMP_SHORT_ERR 0x80000000 -#define CS35L41_BST_SHORT_ERR 0x0100 -#define CS35L41_TEMP_WARN 0x8000 -#define CS35L41_TEMP_ERR 0x00020000 -#define CS35L41_BST_OVP_ERR 0x40 -#define CS35L41_BST_DCM_UVP_ERR 0x80 -#define CS35L41_OTP_BOOT_DONE 0x02 -#define CS35L41_PLL_UNLOCK 0x10 -#define CS35L41_OTP_BOOT_ERR 0x80000000 - -#define CS35L41_AMP_SHORT_ERR_RLS 0x02 -#define CS35L41_BST_SHORT_ERR_RLS 0x04 -#define CS35L41_BST_OVP_ERR_RLS 0x08 -#define CS35L41_BST_UVP_ERR_RLS 0x10 -#define CS35L41_TEMP_WARN_ERR_RLS 0x20 -#define CS35L41_TEMP_ERR_RLS 0x40 - -#define CS35L41_INT1_MASK_DEFAULT 0x7FFCFE3F -#define CS35L41_INT1_UNMASK_PUP 0xFEFFFFFF -#define CS35L41_INT1_UNMASK_PDN 0xFF7FFFFF - -#define CS35L41_GPIO_DIR_MASK 0x80000000 -#define CS35L41_GPIO_DIR_SHIFT 31 -#define CS35L41_GPIO1_CTRL_MASK 0x00030000 -#define CS35L41_GPIO1_CTRL_SHIFT 16 -#define CS35L41_GPIO2_CTRL_MASK 0x07000000 -#define CS35L41_GPIO2_CTRL_SHIFT 24 -#define CS35L41_GPIO_CTRL_OPEN_INT 2 -#define CS35L41_GPIO_CTRL_ACTV_LO 4 -#define CS35L41_GPIO_CTRL_ACTV_HI 5 -#define CS35L41_GPIO_POL_MASK 0x1000 -#define CS35L41_GPIO_POL_SHIFT 12 - -#define CS35L41_AMP_INV_PCM_SHIFT 14 -#define CS35L41_AMP_INV_PCM_MASK BIT(CS35L41_AMP_INV_PCM_SHIFT) -#define CS35L41_AMP_PCM_VOL_SHIFT 3 -#define CS35L41_AMP_PCM_VOL_MASK (0x7FF << 3) -#define CS35L41_AMP_PCM_VOL_MUTE 0x4CF - -#define CS35L41_CHIP_ID 0x35a40 -#define CS35L41R_CHIP_ID 0x35b40 -#define CS35L41_MTLREVID_MASK 0x0F -#define CS35L41_REVID_A0 0xA0 -#define CS35L41_REVID_B0 0xB0 -#define CS35L41_REVID_B2 0xB2 - -#define CS35L41_HALO_CORE_RESET 0x00000200 - -#define CS35L41_FS1_WINDOW_MASK 0x000007FF -#define CS35L41_FS2_WINDOW_MASK 0x00FFF800 -#define CS35L41_FS2_WINDOW_SHIFT 12 - -#define CS35L41_SPI_MAX_FREQ 4000000 +#include "wm_adsp.h" #define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) #define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) -bool cs35l41_readable_reg(struct device *dev, unsigned int reg); -bool cs35l41_precious_reg(struct device *dev, unsigned int reg); -bool cs35l41_volatile_reg(struct device *dev, unsigned int reg); +extern const struct dev_pm_ops cs35l41_pm_ops; -struct cs35l41_otp_packed_element_t { - u32 reg; - u8 shift; - u8 size; +enum cs35l41_cspl_mbox_status { + CSPL_MBOX_STS_RUNNING = 0, + CSPL_MBOX_STS_PAUSED = 1, + CSPL_MBOX_STS_RDY_FOR_REINIT = 2, }; -struct cs35l41_otp_map_element_t { - u32 id; - u32 num_elements; - const struct cs35l41_otp_packed_element_t *map; - u32 bit_offset; - u32 word_offset; +enum cs35l41_cspl_mbox_cmd { + CSPL_MBOX_CMD_NONE = 0, + CSPL_MBOX_CMD_PAUSE = 1, + CSPL_MBOX_CMD_RESUME = 2, + CSPL_MBOX_CMD_REINIT = 3, + CSPL_MBOX_CMD_STOP_PRE_REINIT = 4, + CSPL_MBOX_CMD_HIBERNATE = 5, + CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6, + CSPL_MBOX_CMD_UNKNOWN_CMD = -1, + CSPL_MBOX_CMD_INVALID_SEQUENCE = -2, }; -extern const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG]; -extern const struct cs35l41_otp_map_element_t - cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS]; - -#define CS35L41_REGSTRIDE 4 - struct cs35l41_private { + struct wm_adsp dsp; /* needs to be first member */ struct snd_soc_codec *codec; struct cs35l41_platform_data pdata; struct device *dev; diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index cffd6111afac..4aaee1873a11 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -611,8 +611,8 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, if (devid != CS4265_CHIP_ID_VAL) { ret = -ENODEV; dev_err(&i2c_client->dev, - "CS4265 Device ID (%X). Expected %X\n", - devid, CS4265_CHIP_ID); + "CS4265 Part Number ID: 0x%x Expected: 0x%x\n", + devid >> 4, CS4265_CHIP_ID_VAL >> 4); return ret; } dev_info(&i2c_client->dev, @@ -626,6 +626,16 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, ARRAY_SIZE(cs4265_dai)); } +static int cs4265_i2c_remove(struct i2c_client *i2c) +{ + struct cs4265_private *cs4265 = i2c_get_clientdata(i2c); + + if (cs4265->reset_gpio) + gpiod_set_value_cansleep(cs4265->reset_gpio, 0); + + return 0; +} + static const struct of_device_id cs4265_of_match[] = { { .compatible = "cirrus,cs4265", }, { } @@ -645,6 +655,7 @@ static struct i2c_driver cs4265_i2c_driver = { }, .id_table = cs4265_id, .probe = cs4265_i2c_probe, + .remove = cs4265_i2c_remove, }; module_i2c_driver(cs4265_i2c_driver); diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 27a1c4c73074..43d98bdb5b5b 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -42,6 +42,7 @@ static const struct reg_default cs42l42_reg_defaults[] = { { CS42L42_SRC_CTL, 0x10 }, { CS42L42_MCLK_CTL, 0x02 }, { CS42L42_SFTRAMP_RATE, 0xA4 }, + { CS42L42_SLOW_START_ENABLE, 0x70 }, { CS42L42_I2C_DEBOUNCE, 0x88 }, { CS42L42_I2C_STRETCH, 0x03 }, { CS42L42_I2C_TIMEOUT, 0xB7 }, @@ -177,6 +178,7 @@ static bool cs42l42_readable_register(struct device *dev, unsigned int reg) case CS42L42_MCLK_STATUS: case CS42L42_MCLK_CTL: case CS42L42_SFTRAMP_RATE: + case CS42L42_SLOW_START_ENABLE: case CS42L42_I2C_DEBOUNCE: case CS42L42_I2C_STRETCH: case CS42L42_I2C_TIMEOUT: @@ -387,6 +389,28 @@ static const struct regmap_config cs42l42_regmap = { static DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 100, true); static DECLARE_TLV_DB_SCALE(mixer_tlv, -6300, 100, true); +static int cs42l42_slow_start_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + u8 val; + + /* all bits of SLOW_START_EN much change together */ + switch (ucontrol->value.integer.value[0]) { + case 0: + val = 0; + break; + case 1: + val = CS42L42_SLOW_START_EN_MASK; + break; + default: + return -EINVAL; + } + + return snd_soc_component_update_bits(component, CS42L42_SLOW_START_ENABLE, + CS42L42_SLOW_START_EN_MASK, val); +} + static const char * const cs42l42_hpf_freq_text[] = { "1.86Hz", "120Hz", "235Hz", "466Hz" }; @@ -431,7 +455,11 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = { CS42L42_DAC_HPF_EN_SHIFT, true, false), SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL, CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT, - 0x3f, 1, mixer_tlv) + 0x3f, 1, mixer_tlv), + + SOC_SINGLE_EXT("Slow Start Switch", CS42L42_SLOW_START_ENABLE, + CS42L42_SLOW_START_EN_SHIFT, true, false, + snd_soc_get_volsw, cs42l42_slow_start_put), }; static int cs42l42_hp_adc_ev(struct snd_soc_dapm_widget *w, @@ -521,8 +549,25 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ { struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + /* Prevent race with interrupt handler */ + mutex_lock(&cs42l42->jack_detect_mutex); cs42l42->jack = jk; + if (jk) { + switch (cs42l42->hs_type) { + case CS42L42_PLUG_CTIA: + case CS42L42_PLUG_OMTP: + snd_soc_jack_report(jk, SND_JACK_HEADSET, SND_JACK_HEADSET); + break; + case CS42L42_PLUG_HEADPHONE: + snd_soc_jack_report(jk, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE); + break; + default: + break; + } + } + mutex_unlock(&cs42l42->jack_detect_mutex); + return 0; } @@ -706,10 +751,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component) CS42L42_PLL_DIVOUT_MASK, (pll_ratio_table[i].pll_divout * pll_ratio_table[i].n) << CS42L42_PLL_DIVOUT_SHIFT); - if (pll_ratio_table[i].n != 1) - cs42l42->pll_divout = pll_ratio_table[i].pll_divout; - else - cs42l42->pll_divout = 0; snd_soc_component_update_bits(component, CS42L42_PLL_CAL_RATIO, CS42L42_PLL_CAL_RATIO_MASK, @@ -976,12 +1017,13 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, CS42L42_PLL_START_MASK, 1); - if (cs42l42->pll_divout) { + if (pll_ratio_table[cs42l42->pll_config].n > 1) { usleep_range(CS42L42_PLL_DIVOUT_TIME_US, CS42L42_PLL_DIVOUT_TIME_US * 2); + regval = pll_ratio_table[cs42l42->pll_config].pll_divout; snd_soc_component_update_bits(component, CS42L42_PLL_CTL3, CS42L42_PLL_DIVOUT_MASK, - cs42l42->pll_divout << + regval << CS42L42_PLL_DIVOUT_SHIFT); } @@ -1242,10 +1284,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) /* Turn on level detect circuitry */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (3 << CS42L42_HSBIAS_CTL_SHIFT) | (0 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1272,10 +1312,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) /* Make sure button detect and HS bias circuits are off */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (1 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); } @@ -1296,12 +1334,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) /* Unmask tip sense interrupts */ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, - CS42L42_RS_PLUG_MASK | - CS42L42_RS_UNPLUG_MASK | CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK, - (1 << CS42L42_RS_PLUG_SHIFT) | - (1 << CS42L42_RS_UNPLUG_SHIFT) | (0 << CS42L42_TS_PLUG_SHIFT) | (0 << CS42L42_TS_UNPLUG_SHIFT)); } @@ -1311,22 +1345,16 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42) /* Mask tip sense interrupts */ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, - CS42L42_RS_PLUG_MASK | - CS42L42_RS_UNPLUG_MASK | CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK, - (1 << CS42L42_RS_PLUG_SHIFT) | - (1 << CS42L42_RS_UNPLUG_SHIFT) | (1 << CS42L42_TS_PLUG_SHIFT) | (1 << CS42L42_TS_UNPLUG_SHIFT)); /* Make sure button detect and HS bias circuits are off */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (1 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1370,10 +1398,8 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42) /* Power up HS bias to 2.7V */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (3 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1420,10 +1446,8 @@ static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42) /* Ground HS bias */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (1 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1611,6 +1635,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) CS42L42_M_DETECT_FT_MASK | CS42L42_M_HSBIAS_HIZ_MASK); + mutex_lock(&cs42l42->jack_detect_mutex); + /* Check auto-detect status */ if ((~masks[5]) & irq_params_table[5].mask) { if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) { @@ -1647,18 +1673,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) cs42l42->plug_state = CS42L42_TS_UNPLUG; cs42l42_cancel_hs_type_detect(cs42l42); - switch (cs42l42->hs_type) { - case CS42L42_PLUG_CTIA: - case CS42L42_PLUG_OMTP: - snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADSET); - break; - case CS42L42_PLUG_HEADPHONE: - snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADPHONE); - break; - default: - break; - } snd_soc_jack_report(cs42l42->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3); @@ -1689,6 +1705,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) } } + mutex_unlock(&cs42l42->jack_detect_mutex); + return IRQ_HANDLED; } @@ -1801,6 +1819,9 @@ static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42) cs42l42->hs_type = CS42L42_PLUG_INVALID; + regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, + CS42L42_DETECT_MODE_MASK, 0); + /* Latch analog controls to VP power domain */ regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_LATCH_TO_VP_MASK | @@ -2033,6 +2054,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, cs42l42->dev = &i2c_client->dev; i2c_set_clientdata(i2c_client, cs42l42); + mutex_init(&cs42l42->jack_detect_mutex); cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); if (IS_ERR(cs42l42->regmap)) { diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index f45bcc9a3a62..9fff183dce8e 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -12,6 +12,7 @@ #ifndef __CS42L42_H__ #define __CS42L42_H__ +#include <linux/mutex.h> #include <sound/jack.h> #define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */ @@ -62,6 +63,9 @@ #define CS42L42_INTERNAL_FS_MASK (1 << CS42L42_INTERNAL_FS_SHIFT) #define CS42L42_SFTRAMP_RATE (CS42L42_PAGE_10 + 0x0A) +#define CS42L42_SLOW_START_ENABLE (CS42L42_PAGE_10 + 0x0B) +#define CS42L42_SLOW_START_EN_MASK GENMASK(6, 4) +#define CS42L42_SLOW_START_EN_SHIFT 4 #define CS42L42_I2C_DEBOUNCE (CS42L42_PAGE_10 + 0x0E) #define CS42L42_I2C_STRETCH (CS42L42_PAGE_10 + 0x0F) #define CS42L42_I2C_TIMEOUT (CS42L42_PAGE_10 + 0x10) @@ -838,11 +842,11 @@ struct cs42l42_private { struct gpio_desc *reset_gpio; struct completion pdn_done; struct snd_soc_jack *jack; + struct mutex jack_detect_mutex; int pll_config; int bclk; u32 sclk; u32 srate; - u8 pll_divout; u8 plug_state; u8 hs_type; u8 ts_inv; diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c index 2344a0b03518..9f20bfb855b3 100644 --- a/sound/soc/codecs/es7241.c +++ b/sound/soc/codecs/es7241.c @@ -255,7 +255,6 @@ static int es7241_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct es7241_data *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -271,28 +270,19 @@ static int es7241_probe(struct platform_device *pdev) es7241_parse_fmt(dev, priv); priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(priv->reset)) { - err = PTR_ERR(priv->reset); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'reset' gpio: %d", err); - return err; - } + if (IS_ERR(priv->reset)) + return dev_err_probe(dev, PTR_ERR(priv->reset), + "Failed to get 'reset' gpio"); priv->m0 = devm_gpiod_get_optional(dev, "m0", GPIOD_OUT_LOW); - if (IS_ERR(priv->m0)) { - err = PTR_ERR(priv->m0); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'm0' gpio: %d", err); - return err; - } + if (IS_ERR(priv->m0)) + return dev_err_probe(dev, PTR_ERR(priv->m0), + "Failed to get 'm0' gpio"); priv->m1 = devm_gpiod_get_optional(dev, "m1", GPIOD_OUT_LOW); - if (IS_ERR(priv->m1)) { - err = PTR_ERR(priv->m1); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'm1' gpio: %d", err); - return err; - } + if (IS_ERR(priv->m1)) + return dev_err_probe(dev, PTR_ERR(priv->m1), + "Failed to get 'm1' gpio"); return devm_snd_soc_register_component(&pdev->dev, &es7241_component_driver, diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 390dd6c7f6a5..de5955db0a5f 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -46,9 +46,8 @@ static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); -static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, unsigned int rx_mask, - int slots, int slot_width); +static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream, + int direction); static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, struct snd_soc_dai *dai); @@ -58,7 +57,7 @@ static const struct snd_soc_dai_ops hdac_hda_dai_ops = { .prepare = hdac_hda_dai_prepare, .hw_params = hdac_hda_dai_hw_params, .hw_free = hdac_hda_dai_hw_free, - .set_tdm_slot = hdac_hda_dai_set_tdm_slot, + .set_stream = hdac_hda_dai_set_stream, }; static struct snd_soc_dai_driver hdac_hda_dais[] = { @@ -180,21 +179,22 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = { }; -static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, unsigned int rx_mask, - int slots, int slot_width) +static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) { struct snd_soc_component *component = dai->component; struct hdac_hda_priv *hda_pvt; struct hdac_hda_pcm *pcm; + struct hdac_stream *hstream; + + if (!stream) + return -EINVAL; hda_pvt = snd_soc_component_get_drvdata(component); pcm = &hda_pvt->pcm[dai->id]; + hstream = (struct hdac_stream *)stream; - if (tx_mask) - pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; - else - pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; + pcm->stream_tag[direction] = hstream->stream_tag; return 0; } diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c index 6b60120f59a6..1d0c467ab57b 100644 --- a/sound/soc/codecs/jz4770.c +++ b/sound/soc/codecs/jz4770.c @@ -307,6 +307,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0); static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600); static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0); +static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0); /* Unconditional controls. */ static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { @@ -319,6 +320,14 @@ static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume", JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR, REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv), + + SOC_SINGLE_TLV("Mixer Capture Volume", + JZ4770_CODEC_REG_GCR_MIXADC, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), + + SOC_SINGLE_TLV("Mixer Playback Volume", + JZ4770_CODEC_REG_GCR_MIXDAC, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), }; static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = { diff --git a/sound/soc/codecs/max9759.c b/sound/soc/codecs/max9759.c index 00e9d4fd1651..d75fd61b9032 100644 --- a/sound/soc/codecs/max9759.c +++ b/sound/soc/codecs/max9759.c @@ -140,7 +140,6 @@ static int max9759_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct max9759 *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -149,29 +148,20 @@ static int max9759_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpiod_shutdown)) { - err = PTR_ERR(priv->gpiod_shutdown); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'shutdown' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_shutdown)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown), + "Failed to get 'shutdown' gpio"); priv->gpiod_mute = devm_gpiod_get(dev, "mute", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpiod_mute)) { - err = PTR_ERR(priv->gpiod_mute); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'mute' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_mute)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute), + "Failed to get 'mute' gpio"); priv->is_mute = true; priv->gpiod_gain = devm_gpiod_get_array(dev, "gain", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpiod_gain)) { - err = PTR_ERR(priv->gpiod_gain); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'gain' gpios: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_gain)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_gain), + "Failed to get 'gain' gpios"); priv->gain = 0; if (priv->gpiod_gain->ndescs != 2) { diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index dc520effc61c..f47e956d4f55 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -741,7 +741,7 @@ static int max98373_sdw_set_tdm_slot(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops max98373_dai_sdw_ops = { .hw_params = max98373_sdw_dai_hw_params, .hw_free = max98373_pcm_hw_free, - .set_sdw_stream = max98373_set_sdw_stream, + .set_stream = max98373_set_sdw_stream, .shutdown = max98373_shutdown, .set_tdm_slot = max98373_sdw_set_tdm_slot, }; diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index dd29b183ecd6..7c9686be59d9 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -606,12 +606,9 @@ static int max9860_probe(struct i2c_client *i2c) return -ENOMEM; max9860->dvddio = devm_regulator_get(dev, "DVDDIO"); - if (IS_ERR(max9860->dvddio)) { - ret = PTR_ERR(max9860->dvddio); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get DVDDIO supply: %d\n", ret); - return ret; - } + if (IS_ERR(max9860->dvddio)) + return dev_err_probe(dev, PTR_ERR(max9860->dvddio), + "Failed to get DVDDIO supply\n"); max9860->dvddio_nb.notifier_call = max9860_dvddio_event; @@ -643,8 +640,7 @@ static int max9860_probe(struct i2c_client *i2c) if (IS_ERR(mclk)) { ret = PTR_ERR(mclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get MCLK: %d\n", ret); + dev_err_probe(dev, ret, "Failed to get MCLK\n"); goto err_regulator; } diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 3ddd822240e3..485cda46dbb9 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -822,8 +822,8 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = { {"EAR PA", NULL, "EAR CP"}, /* Headset (RX MIX1 and RX MIX2) */ - {"HEADPHONE", NULL, "HPHL PA"}, - {"HEADPHONE", NULL, "HPHR PA"}, + {"HPH_L", NULL, "HPHL PA"}, + {"HPH_R", NULL, "HPHR PA"}, {"HPHL DAC", NULL, "EAR_HPHL_CLK"}, {"HPHR DAC", NULL, "EAR_HPHR_CLK"}, @@ -870,7 +870,8 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { SND_SOC_DAPM_INPUT("AMIC3"), SND_SOC_DAPM_INPUT("AMIC2"), SND_SOC_DAPM_OUTPUT("EAR"), - SND_SOC_DAPM_OUTPUT("HEADPHONE"), + SND_SOC_DAPM_OUTPUT("HPH_L"), + SND_SOC_DAPM_OUTPUT("HPH_R"), /* RX stuff */ SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index 358c500377df..3a881523c30f 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -47,13 +47,12 @@ static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val) struct mt6660_chip *chip = context; int size = mt6660_get_reg_size(reg); u8 reg_data[4]; - int i, ret; + int i; for (i = 0; i < size; i++) reg_data[size - i - 1] = (val >> (8 * i)) & 0xff; - ret = i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data); - return ret; + return i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data); } static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val) diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index b6fd412441a1..fdf92c8b28e1 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -751,21 +751,14 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); - if (IS_ERR(pcm3168a->gpio_rst)) { - ret = PTR_ERR(pcm3168a->gpio_rst); - if (ret != -EPROBE_DEFER ) - dev_err(dev, "failed to acquire RST gpio: %d\n", ret); - - return ret; - } + if (IS_ERR(pcm3168a->gpio_rst)) + return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst), + "failed to acquire RST gpio\n"); pcm3168a->scki = devm_clk_get(dev, "scki"); - if (IS_ERR(pcm3168a->scki)) { - ret = PTR_ERR(pcm3168a->scki); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to acquire clock 'scki': %d\n", ret); - return ret; - } + if (IS_ERR(pcm3168a->scki)) + return dev_err_probe(dev, PTR_ERR(pcm3168a->scki), + "failed to acquire clock 'scki'\n"); ret = clk_prepare_enable(pcm3168a->scki); if (ret) { @@ -781,8 +774,7 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to request supplies: %d\n", ret); + dev_err_probe(dev, ret, "failed to request supplies\n"); goto err_clk; } diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index f716668de640..149a76075c76 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -613,7 +613,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1308 = { static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { .hw_params = rt1308_sdw_hw_params, .hw_free = rt1308_sdw_pcm_hw_free, - .set_sdw_stream = rt1308_set_sdw_stream, + .set_stream = rt1308_set_sdw_stream, .shutdown = rt1308_sdw_shutdown, .set_tdm_slot = rt1308_sdw_set_tdm_slot, }; diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 09b4914bba1b..c66d7b20cb4d 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -602,7 +602,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1316 = { static const struct snd_soc_dai_ops rt1316_aif_dai_ops = { .hw_params = rt1316_sdw_hw_params, .hw_free = rt1316_sdw_pcm_hw_free, - .set_sdw_stream = rt1316_set_sdw_stream, + .set_stream = rt1316_set_sdw_stream, .shutdown = rt1316_sdw_shutdown, }; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index d01fe73ab9c8..e7a82565b905 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -195,6 +195,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg) case RT5640_PRIV_DATA: case RT5640_PGM_REG_ARR1: case RT5640_PGM_REG_ARR3: + case RT5640_DUMMY2: case RT5640_VENDOR_ID: case RT5640_VENDOR_ID1: case RT5640_VENDOR_ID2: @@ -1972,7 +1973,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, RT5640_PWR_FV1 | RT5640_PWR_FV2, RT5640_PWR_FV1 | RT5640_PWR_FV2); snd_soc_component_update_bits(component, RT5640_DUMMY1, - 0x0301, 0x0301); + 0x1, 0x1); snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030); } @@ -2159,7 +2160,11 @@ static bool rt5640_jack_inserted(struct snd_soc_component *component) struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); int val; - val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + if (rt5640->jd_gpio) + val = gpiod_get_value(rt5640->jd_gpio) ? RT5640_JD_STATUS : 0; + else + val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + dev_dbg(component->dev, "irq status %#04x\n", val); if (rt5640->jd_inverted) @@ -2297,10 +2302,42 @@ EXPORT_SYMBOL_GPL(rt5640_detect_headset); static void rt5640_jack_work(struct work_struct *work) { struct rt5640_priv *rt5640 = - container_of(work, struct rt5640_priv, jack_work); + container_of(work, struct rt5640_priv, jack_work.work); struct snd_soc_component *component = rt5640->component; int status; + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) { + int val, jack_type = 0, hda_mic_plugged, hda_hp_plugged; + + /* mic jack */ + val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + hda_mic_plugged = !(val & RT5640_JD_STATUS); + dev_dbg(component->dev, "mic jack status %d\n", + hda_mic_plugged); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL1, + RT5640_JD_P_MASK, !hda_mic_plugged << RT5640_JD_P_SFT); + + if (hda_mic_plugged) + jack_type |= SND_JACK_MICROPHONE; + + /* headphone jack */ + val = snd_soc_component_read(component, RT5640_DUMMY2); + hda_hp_plugged = !(val & (0x1 << 11)); + dev_dbg(component->dev, "headphone jack status %d\n", + hda_hp_plugged); + + snd_soc_component_update_bits(component, RT5640_DUMMY2, + (0x1 << 10), !hda_hp_plugged << 10); + + if (hda_hp_plugged) + jack_type |= SND_JACK_HEADPHONE; + + snd_soc_jack_report(rt5640->jack, jack_type, SND_JACK_HEADSET); + + return; + } + if (!rt5640_jack_inserted(component)) { /* Jack removed, or spurious IRQ? */ if (rt5640->jack->status & SND_JACK_HEADPHONE) { @@ -2348,7 +2385,7 @@ static void rt5640_jack_work(struct work_struct *work) * disabled the OVCD IRQ, the IRQ pin will stay high and as * we react to edges, we miss the unplug event -> recheck. */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } } @@ -2357,7 +2394,17 @@ static irqreturn_t rt5640_irq(int irq, void *data) struct rt5640_priv *rt5640 = data; if (rt5640->jack) - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + + return IRQ_HANDLED; +} + +static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data) +{ + struct rt5640_priv *rt5640 = data; + + queue_delayed_work(system_long_wq, &rt5640->jack_work, + msecs_to_jiffies(JACK_SETTLE_TIME)); return IRQ_HANDLED; } @@ -2366,7 +2413,7 @@ static void rt5640_cancel_work(void *data) { struct rt5640_priv *rt5640 = data; - cancel_work_sync(&rt5640->jack_work); + cancel_delayed_work_sync(&rt5640->jack_work); cancel_delayed_work_sync(&rt5640->bp_work); } @@ -2406,7 +2453,12 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) if (!rt5640->jack) return; - free_irq(rt5640->irq, rt5640); + if (rt5640->jd_gpio_irq_requested) + free_irq(rt5640->jd_gpio_irq, rt5640); + + if (rt5640->irq_requested) + free_irq(rt5640->irq, rt5640); + rt5640_cancel_work(rt5640); if (rt5640->jack->status & SND_JACK_MICROPHONE) { @@ -2415,11 +2467,15 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); } + rt5640->jd_gpio_irq_requested = false; + rt5640->irq_requested = false; + rt5640->jd_gpio = NULL; rt5640->jack = NULL; } static void rt5640_enable_jack_detect(struct snd_soc_component *component, - struct snd_soc_jack *jack) + struct snd_soc_jack *jack, + struct rt5640_set_jack_data *jack_data) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); int ret; @@ -2463,28 +2519,90 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, rt5640_enable_micbias1_ovcd_irq(component); } + if (jack_data && jack_data->codec_irq_override) + rt5640->irq = jack_data->codec_irq_override; + + if (jack_data && jack_data->jd_gpio) { + rt5640->jd_gpio = jack_data->jd_gpio; + rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio); + + ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "rt5640-jd-gpio", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n", + rt5640->jd_gpio_irq, ret); + rt5640_disable_jack_detect(component); + return; + } + rt5640->jd_gpio_irq_requested = true; + } + ret = request_irq(rt5640->irq, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5640", rt5640); if (ret) { dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); - rt5640->irq = -ENXIO; - /* Undo above settings */ rt5640_disable_jack_detect(component); return; } + rt5640->irq_requested = true; + + /* sync initial jack state */ + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); +} + +static void rt5640_enable_hda_jack_detect( + struct snd_soc_component *component, struct snd_soc_jack *jack) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int ret; + + /* Select JD1 for Mic */ + snd_soc_component_update_bits(component, RT5640_JD_CTRL, + RT5640_JD_MASK, RT5640_JD_JD1_IN4P); + snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR); + + /* Select JD2 for Headphone */ + snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100); + + /* Selecting GPIO01 as an interrupt */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1, + RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ); + + /* Set GPIO1 output */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, + RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); + + snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0); + + rt5640->jack = jack; + + ret = request_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); + rt5640->irq = -ENXIO; + return; + } /* sync initial jack state */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } static int rt5640_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *data) { - if (jack) - rt5640_enable_jack_detect(component, jack); - else + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + if (jack) { + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) + rt5640_enable_hda_jack_detect(component, jack); + else + rt5640_enable_jack_detect(component, jack, data); + } else { rt5640_disable_jack_detect(component); + } return 0; } @@ -2574,11 +2692,16 @@ static int rt5640_probe(struct snd_soc_component *component) if (device_property_read_u32(component->dev, "realtek,jack-detect-source", &val) == 0) { - if (val <= RT5640_JD_SRC_GPIO4) + if (val <= RT5640_JD_SRC_GPIO4) { rt5640->jd_src = val << RT5640_JD_SFT; - else + } else if (val == RT5640_JD_SRC_HDA_HEADER) { + rt5640->jd_src = RT5640_JD_SRC_HDA_HEADER; + snd_soc_component_update_bits(component, RT5640_DUMMY1, + 0x0300, 0x0); + } else { dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n", val); + } } if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted")) @@ -2632,6 +2755,7 @@ static int rt5640_suspend(struct snd_soc_component *component) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + rt5640_cancel_work(rt5640); snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); rt5640_reset(component); regcache_cache_only(rt5640->regmap, true); @@ -2654,6 +2778,17 @@ static int rt5640_resume(struct snd_soc_component *component) regcache_cache_only(rt5640->regmap, false); regcache_sync(rt5640->regmap); + if (rt5640->jack) { + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) + snd_soc_component_update_bits(component, + RT5640_DUMMY2, 0x1100, 0x1100); + else + snd_soc_component_write(component, RT5640_DUMMY2, + 0x4001); + + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + } + return 0; } #else @@ -2856,7 +2991,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, rt5640->hp_mute = true; rt5640->irq = i2c->irq; INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work); - INIT_WORK(&rt5640->jack_work, rt5640_jack_work); + INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work); /* Make sure work is stopped on probe-error / remove */ ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 2c28f83e338a..9e49b9a0ccaa 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2124,6 +2124,7 @@ struct rt5640_priv { int ldo1_en; /* GPIO for LDO1_EN */ int irq; + int jd_gpio_irq; int sysclk; int sysclk_src; int lrck[RT5640_AIFS]; @@ -2136,6 +2137,8 @@ struct rt5640_priv { bool hp_mute; bool asrc_en; + bool irq_requested; + bool jd_gpio_irq_requested; /* Jack and button detect data */ bool ovcd_irq_enabled; @@ -2145,14 +2148,20 @@ struct rt5640_priv { int release_count; int poll_count; struct delayed_work bp_work; - struct work_struct jack_work; + struct delayed_work jack_work; struct snd_soc_jack *jack; + struct gpio_desc *jd_gpio; unsigned int jd_src; bool jd_inverted; unsigned int ovcd_th; unsigned int ovcd_sf; }; +struct rt5640_set_jack_data { + int codec_irq_override; + struct gpio_desc *jd_gpio; +}; + int rt5640_dmic_enable(struct snd_soc_component *component, bool dmic1_data_pin, bool dmic2_data_pin); int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 0389b2bb360e..2138f62e6af5 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3461,6 +3461,7 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663) static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) { int table_size; + int ret; device_property_read_u32(dev, "realtek,dc_offset_l_manual", &rt5663->pdata.dc_offset_l_manual); @@ -3477,9 +3478,11 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) table_size = sizeof(struct impedance_mapping_table) * rt5663->pdata.impedance_sensing_num; rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL); - device_property_read_u32_array(dev, + ret = device_property_read_u32_array(dev, "realtek,impedance_sensing_table", (u32 *)rt5663->imp_table, table_size); + if (ret) + return ret; } return 0; @@ -3504,8 +3507,11 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, if (pdata) rt5663->pdata = *pdata; - else - rt5663_parse_dp(rt5663, &i2c->dev); + else { + ret = rt5663_parse_dp(rt5663, &i2c->dev); + if (ret) + return ret; + } for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) rt5663->supplies[i].supply = rt5663_supply_names[i]; diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 31a4f286043e..248257a2e4e0 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -272,7 +272,7 @@ static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt5682_sdw_ops = { .hw_params = rt5682_sdw_hw_params, .hw_free = rt5682_sdw_hw_free, - .set_sdw_stream = rt5682_set_sdw_stream, + .set_stream = rt5682_set_sdw_stream, .shutdown = rt5682_sdw_shutdown, }; diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index b34a8542077d..415ec564c82e 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2862,7 +2862,6 @@ int rt5682_register_dai_clks(struct rt5682_priv *rt5682) for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) { struct clk_init_data init = { }; - struct clk_parent_data parent_data; const struct clk_hw *parent; dai_clk_hw = &rt5682->dai_clks_hw[i]; @@ -2871,10 +2870,8 @@ int rt5682_register_dai_clks(struct rt5682_priv *rt5682) case RT5682_DAI_WCLK_IDX: /* Make MCLK the parent of WCLK */ if (rt5682->mclk) { - parent_data = (struct clk_parent_data){ - .fw_name = "mclk", - }; - init.parent_data = &parent_data; + parent = __clk_get_hw(rt5682->mclk); + init.parent_hws = &parent; init.num_parents = 1; } break; diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index d49a4f68566d..efa1016831dd 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -1367,6 +1367,31 @@ static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt5682s_stereo1_adc_mixl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + unsigned int delay = 0; + + if (rt5682s->pdata.amic_delay) + delay = rt5682s->pdata.amic_delay; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(delay); + snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL, + RT5682S_L_MUTE, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL, + RT5682S_L_MUTE, RT5682S_L_MUTE); + break; + } + + return 0; +} + static int sar_power_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1680,9 +1705,10 @@ static const struct snd_soc_dapm_widget rt5682s_dapm_widgets[] = { /* ADC Mixer */ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682S_PWR_DIG_2, RT5682S_PWR_ADC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU), - SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5682S_STO1_ADC_DIG_VOL, - RT5682S_L_MUTE_SFT, 1, rt5682s_sto1_adc_l_mix, - ARRAY_SIZE(rt5682s_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER_E("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5682s_sto1_adc_l_mix, ARRAY_SIZE(rt5682s_sto1_adc_l_mix), + rt5682s_stereo1_adc_mixl_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682S_STO1_ADC_DIG_VOL, RT5682S_R_MUTE_SFT, 1, rt5682s_sto1_adc_r_mix, ARRAY_SIZE(rt5682s_sto1_adc_r_mix)), @@ -2885,6 +2911,8 @@ static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev) &rt5682s->pdata.dmic_clk_rate); device_property_read_u32(dev, "realtek,dmic-delay-ms", &rt5682s->pdata.dmic_delay); + device_property_read_u32(dev, "realtek,amic-delay-ms", + &rt5682s->pdata.amic_delay); rt5682s->pdata.ldo1_en = of_get_named_gpio(dev->of_node, "realtek,ldo1-en-gpios", 0); diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 921382724f9c..e61a8257bf64 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1005,7 +1005,7 @@ static int rt700_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt700_ops = { .hw_params = rt700_pcm_hw_params, .hw_free = rt700_pcm_hw_free, - .set_sdw_stream = rt700_set_sdw_stream, + .set_stream = rt700_set_sdw_stream, .shutdown = rt700_shutdown, }; diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 2e992589f1e4..bdb1375f0338 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -1358,7 +1358,7 @@ static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt711_sdca_ops = { .hw_params = rt711_sdca_pcm_hw_params, .hw_free = rt711_sdca_pcm_hw_free, - .set_sdw_stream = rt711_sdca_set_sdw_stream, + .set_stream = rt711_sdca_set_sdw_stream, .shutdown = rt711_sdca_shutdown, }; diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index a7c5608a0ef8..6770825d037a 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -1089,7 +1089,7 @@ static int rt711_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt711_ops = { .hw_params = rt711_pcm_hw_params, .hw_free = rt711_pcm_hw_free, - .set_sdw_stream = rt711_set_sdw_stream, + .set_stream = rt711_set_sdw_stream, .shutdown = rt711_shutdown, }; diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 66e166568c50..bfa536bd7196 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -938,7 +938,7 @@ static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt715_sdca_ops = { .hw_params = rt715_sdca_pcm_hw_params, .hw_free = rt715_sdca_pcm_hw_free, - .set_sdw_stream = rt715_sdca_set_sdw_stream, + .set_stream = rt715_sdca_set_sdw_stream, .shutdown = rt715_sdca_shutdown, }; diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 1352869cc086..a64d11a74751 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -909,7 +909,7 @@ static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt715_ops = { .hw_params = rt715_pcm_hw_params, .hw_free = rt715_pcm_hw_free, - .set_sdw_stream = rt715_set_sdw_stream, + .set_stream = rt715_set_sdw_stream, .shutdown = rt715_shutdown, }; diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c index 8ea13cfa9f8e..7c612aaf31c7 100644 --- a/sound/soc/codecs/sdw-mockup.c +++ b/sound/soc/codecs/sdw-mockup.c @@ -138,7 +138,7 @@ static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops sdw_mockup_ops = { .hw_params = sdw_mockup_pcm_hw_params, .hw_free = sdw_mockup_pcm_hw_free, - .set_sdw_stream = sdw_mockup_set_sdw_stream, + .set_stream = sdw_mockup_set_sdw_stream, .shutdown = sdw_mockup_shutdown, }; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 97bf1f222805..8eebf27d0ea2 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1612,9 +1612,8 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, if (ret == -ENOENT) ret = -EPROBE_DEFER; - if (ret != -EPROBE_DEFER) - dev_err(&client->dev, "Failed to get mclock: %d\n", - ret); + dev_err_probe(&client->dev, ret, "Failed to get mclock\n"); + goto disable_regs; } diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c index b30fc1f894e1..d306c585b52b 100644 --- a/sound/soc/codecs/simple-amplifier.c +++ b/sound/soc/codecs/simple-amplifier.c @@ -69,7 +69,6 @@ static int simple_amp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct simple_amp *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (priv == NULL) @@ -78,12 +77,9 @@ static int simple_amp_probe(struct platform_device *pdev) priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_enable)) { - err = PTR_ERR(priv->gpiod_enable); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'enable' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_enable)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_enable), + "Failed to get 'enable' gpio"); return devm_snd_soc_register_component(dev, &simple_amp_component_driver, diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c index e0a09dadfa7c..d30c0d24d90a 100644 --- a/sound/soc/codecs/simple-mux.c +++ b/sound/soc/codecs/simple-mux.c @@ -82,7 +82,6 @@ static int simple_mux_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct simple_mux *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -91,12 +90,9 @@ static int simple_mux_probe(struct platform_device *pdev) dev_set_drvdata(dev, priv); priv->gpiod_mux = devm_gpiod_get(dev, "mux", GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_mux)) { - err = PTR_ERR(priv->gpiod_mux); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'mux' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_mux)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_mux), + "Failed to get 'mux' gpio"); return devm_snd_soc_register_component(dev, &simple_mux_component_driver, NULL, 0); } diff --git a/sound/soc/codecs/ssm2305.c b/sound/soc/codecs/ssm2305.c index 2968959c4b75..1d022643c307 100644 --- a/sound/soc/codecs/ssm2305.c +++ b/sound/soc/codecs/ssm2305.c @@ -57,7 +57,6 @@ static int ssm2305_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ssm2305 *priv; - int err; /* Allocate the private data */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -69,13 +68,9 @@ static int ssm2305_probe(struct platform_device *pdev) /* Get shutdown gpio */ priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_shutdown)) { - err = PTR_ERR(priv->gpiod_shutdown); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'shutdown' gpio: %d\n", - err); - return err; - } + if (IS_ERR(priv->gpiod_shutdown)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown), + "Failed to get 'shutdown' gpio\n"); return devm_snd_soc_register_component(dev, &ssm2305_component_driver, NULL, 0); diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h index f16900e00afa..80bf56093d94 100644 --- a/sound/soc/codecs/sta350.h +++ b/sound/soc/codecs/sta350.h @@ -14,7 +14,7 @@ #ifndef _ASOC_STA_350_H #define _ASOC_STA_350_H -/* STA50 register addresses */ +/* STA350 register addresses */ #define STA350_REGISTER_COUNT 0x4D #define STA350_COEF_COUNT 62 diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c index eb2a7870148d..dc86852752c5 100644 --- a/sound/soc/codecs/tfa989x.c +++ b/sound/soc/codecs/tfa989x.c @@ -7,6 +7,7 @@ * Copyright (C) 2013 Sony Mobile Communications Inc. */ +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/regmap.h> @@ -56,6 +57,7 @@ struct tfa989x_rev { struct tfa989x { const struct tfa989x_rev *rev; struct regulator *vddd_supply; + struct gpio_desc *rcv_gpiod; }; static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg) @@ -99,10 +101,20 @@ static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = { {"Amp Input", "Right", "AIFINR"}, }; +static int tfa989x_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + gpiod_set_value_cansleep(tfa989x->rcv_gpiod, ucontrol->value.enumerated.item[0]); + + return snd_soc_put_enum_double(kcontrol, ucontrol); +} + static const char * const mode_text[] = { "Speaker", "Receiver" }; static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text); static const struct snd_kcontrol_new tfa989x_mode_controls[] = { - SOC_ENUM("Mode", mode_enum), + SOC_ENUM_EXT("Mode", mode_enum, snd_soc_get_enum_double, tfa989x_put_mode), }; static int tfa989x_probe(struct snd_soc_component *component) @@ -301,6 +313,12 @@ static int tfa989x_i2c_probe(struct i2c_client *i2c) return dev_err_probe(dev, PTR_ERR(tfa989x->vddd_supply), "Failed to get vddd regulator\n"); + if (tfa989x->rev->rev == TFA9897_REVISION) { + tfa989x->rcv_gpiod = devm_gpiod_get_optional(dev, "rcv", GPIOD_OUT_LOW); + if (IS_ERR(tfa989x->rcv_gpiod)) + return PTR_ERR(tfa989x->rcv_gpiod); + } + regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap); if (IS_ERR(regmap)) return PTR_ERR(regmap); diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c new file mode 100644 index 000000000000..4baf3d881633 --- /dev/null +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -0,0 +1,1317 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Based on sound/soc/codecs/tlv320aic3x.c by Vladimir Barinov +// +// Copyright (C) 2010 Mistral Solutions Pvt Ltd. +// Author: Shahina Shaik <shahina.s@mistralsolutions.com> +// +// Copyright (C) 2014-2018, Ambarella, Inc. +// Author: Dongge wu <dgwu@ambarella.com> +// +// Copyright (C) 2021 Axis Communications AB +// Author: Ricard Wanderlof <ricardw@axis.com> +// + +#include <dt-bindings/sound/tlv320adc3xxx.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/gpio/driver.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/cdev.h> +#include <linux/of_gpio.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include <sound/initval.h> + +/* + * General definitions defining exported functionality. + */ + +#define ADC3XXX_MICBIAS_PINS 2 + +/* Number of GPIO pins exposed via the gpiolib interface */ +#define ADC3XXX_GPIOS_MAX 2 + +#define ADC3XXX_RATES SNDRV_PCM_RATE_8000_96000 +#define ADC3XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* + * PLL modes, to be used for clk_id for set_sysclk callback. + * + * The default behavior (AUTO) is to take the first matching entry in the clock + * table, which is intended to be the PLL based one if there is more than one. + * + * Setting the clock source using simple-card (clocks or + * system-clock-frequency property) sets clk_id = 0 = ADC3XXX_PLL_AUTO. + */ +#define ADC3XXX_PLL_AUTO 0 /* Use first available mode */ +#define ADC3XXX_PLL_ENABLE 1 /* Use PLL for clock generation */ +#define ADC3XXX_PLL_BYPASS 2 /* Don't use PLL for clock generation */ + +/* Register definitions. */ + +#define ADC3XXX_PAGE_SIZE 128 +#define ADC3XXX_REG(page, reg) ((page * ADC3XXX_PAGE_SIZE) + reg) + +/* + * Page 0 registers. + */ + +#define ADC3XXX_PAGE_SELECT ADC3XXX_REG(0, 0) +#define ADC3XXX_RESET ADC3XXX_REG(0, 1) + +/* 2-3 Reserved */ + +#define ADC3XXX_CLKGEN_MUX ADC3XXX_REG(0, 4) +#define ADC3XXX_PLL_PROG_PR ADC3XXX_REG(0, 5) +#define ADC3XXX_PLL_PROG_J ADC3XXX_REG(0, 6) +#define ADC3XXX_PLL_PROG_D_MSB ADC3XXX_REG(0, 7) +#define ADC3XXX_PLL_PROG_D_LSB ADC3XXX_REG(0, 8) + +/* 9-17 Reserved */ + +#define ADC3XXX_ADC_NADC ADC3XXX_REG(0, 18) +#define ADC3XXX_ADC_MADC ADC3XXX_REG(0, 19) +#define ADC3XXX_ADC_AOSR ADC3XXX_REG(0, 20) +#define ADC3XXX_ADC_IADC ADC3XXX_REG(0, 21) + +/* 23-24 Reserved */ + +#define ADC3XXX_CLKOUT_MUX ADC3XXX_REG(0, 25) +#define ADC3XXX_CLKOUT_M_DIV ADC3XXX_REG(0, 26) +#define ADC3XXX_INTERFACE_CTRL_1 ADC3XXX_REG(0, 27) +#define ADC3XXX_CH_OFFSET_1 ADC3XXX_REG(0, 28) +#define ADC3XXX_INTERFACE_CTRL_2 ADC3XXX_REG(0, 29) +#define ADC3XXX_BCLK_N_DIV ADC3XXX_REG(0, 30) +#define ADC3XXX_INTERFACE_CTRL_3 ADC3XXX_REG(0, 31) +#define ADC3XXX_INTERFACE_CTRL_4 ADC3XXX_REG(0, 32) +#define ADC3XXX_INTERFACE_CTRL_5 ADC3XXX_REG(0, 33) +#define ADC3XXX_I2S_SYNC ADC3XXX_REG(0, 34) +/* 35 Reserved */ +#define ADC3XXX_ADC_FLAG ADC3XXX_REG(0, 36) +#define ADC3XXX_CH_OFFSET_2 ADC3XXX_REG(0, 37) +#define ADC3XXX_I2S_TDM_CTRL ADC3XXX_REG(0, 38) +/* 39-41 Reserved */ +#define ADC3XXX_INTR_FLAG_1 ADC3XXX_REG(0, 42) +#define ADC3XXX_INTR_FLAG_2 ADC3XXX_REG(0, 43) +/* 44 Reserved */ +#define ADC3XXX_INTR_FLAG_ADC1 ADC3XXX_REG(0, 45) +/* 46 Reserved */ +#define ADC3XXX_INTR_FLAG_ADC2 ADC3XXX_REG(0, 47) +#define ADC3XXX_INT1_CTRL ADC3XXX_REG(0, 48) +#define ADC3XXX_INT2_CTRL ADC3XXX_REG(0, 49) +/* 50 Reserved */ +#define ADC3XXX_GPIO2_CTRL ADC3XXX_REG(0, 51) +#define ADC3XXX_GPIO1_CTRL ADC3XXX_REG(0, 52) +#define ADC3XXX_DOUT_CTRL ADC3XXX_REG(0, 53) +/* 54-56 Reserved */ +#define ADC3XXX_SYNC_CTRL_1 ADC3XXX_REG(0, 57) +#define ADC3XXX_SYNC_CTRL_2 ADC3XXX_REG(0, 58) +#define ADC3XXX_CIC_GAIN_CTRL ADC3XXX_REG(0, 59) +/* 60 Reserved */ +#define ADC3XXX_PRB_SELECT ADC3XXX_REG(0, 61) +#define ADC3XXX_INST_MODE_CTRL ADC3XXX_REG(0, 62) +/* 63-79 Reserved */ +#define ADC3XXX_MIC_POLARITY_CTRL ADC3XXX_REG(0, 80) +#define ADC3XXX_ADC_DIGITAL ADC3XXX_REG(0, 81) +#define ADC3XXX_ADC_FGA ADC3XXX_REG(0, 82) +#define ADC3XXX_LADC_VOL ADC3XXX_REG(0, 83) +#define ADC3XXX_RADC_VOL ADC3XXX_REG(0, 84) +#define ADC3XXX_ADC_PHASE_COMP ADC3XXX_REG(0, 85) +#define ADC3XXX_LEFT_CHN_AGC_1 ADC3XXX_REG(0, 86) +#define ADC3XXX_LEFT_CHN_AGC_2 ADC3XXX_REG(0, 87) +#define ADC3XXX_LEFT_CHN_AGC_3 ADC3XXX_REG(0, 88) +#define ADC3XXX_LEFT_CHN_AGC_4 ADC3XXX_REG(0, 89) +#define ADC3XXX_LEFT_CHN_AGC_5 ADC3XXX_REG(0, 90) +#define ADC3XXX_LEFT_CHN_AGC_6 ADC3XXX_REG(0, 91) +#define ADC3XXX_LEFT_CHN_AGC_7 ADC3XXX_REG(0, 92) +#define ADC3XXX_LEFT_AGC_GAIN ADC3XXX_REG(0, 93) +#define ADC3XXX_RIGHT_CHN_AGC_1 ADC3XXX_REG(0, 94) +#define ADC3XXX_RIGHT_CHN_AGC_2 ADC3XXX_REG(0, 95) +#define ADC3XXX_RIGHT_CHN_AGC_3 ADC3XXX_REG(0, 96) +#define ADC3XXX_RIGHT_CHN_AGC_4 ADC3XXX_REG(0, 97) +#define ADC3XXX_RIGHT_CHN_AGC_5 ADC3XXX_REG(0, 98) +#define ADC3XXX_RIGHT_CHN_AGC_6 ADC3XXX_REG(0, 99) +#define ADC3XXX_RIGHT_CHN_AGC_7 ADC3XXX_REG(0, 100) +#define ADC3XXX_RIGHT_AGC_GAIN ADC3XXX_REG(0, 101) +/* 102-127 Reserved */ + +/* + * Page 1 registers. + */ + +/* 1-25 Reserved */ +#define ADC3XXX_DITHER_CTRL ADC3XXX_REG(1, 26) +/* 27-50 Reserved */ +#define ADC3XXX_MICBIAS_CTRL ADC3XXX_REG(1, 51) +#define ADC3XXX_LEFT_PGA_SEL_1 ADC3XXX_REG(1, 52) +/* 53 Reserved */ +#define ADC3XXX_LEFT_PGA_SEL_2 ADC3XXX_REG(1, 54) +#define ADC3XXX_RIGHT_PGA_SEL_1 ADC3XXX_REG(1, 55) +#define ADC3XXX_RIGHT_PGA_SEL_2 ADC3XXX_REG(1, 57) +#define ADC3XXX_LEFT_APGA_CTRL ADC3XXX_REG(1, 59) +#define ADC3XXX_RIGHT_APGA_CTRL ADC3XXX_REG(1, 60) +#define ADC3XXX_LOW_CURRENT_MODES ADC3XXX_REG(1, 61) +#define ADC3XXX_ANALOG_PGA_FLAGS ADC3XXX_REG(1, 62) +/* 63-127 Reserved */ + +/* + * Register bits. + */ + +/* PLL Enable bits */ +#define ADC3XXX_ENABLE_PLL_SHIFT 7 +#define ADC3XXX_ENABLE_PLL (1 << ADC3XXX_ENABLE_PLL_SHIFT) +#define ADC3XXX_ENABLE_NADC_SHIFT 7 +#define ADC3XXX_ENABLE_NADC (1 << ADC3XXX_ENABLE_NADC_SHIFT) +#define ADC3XXX_ENABLE_MADC_SHIFT 7 +#define ADC3XXX_ENABLE_MADC (1 << ADC3XXX_ENABLE_MADC_SHIFT) +#define ADC3XXX_ENABLE_BCLK_SHIFT 7 +#define ADC3XXX_ENABLE_BCLK (1 << ADC3XXX_ENABLE_BCLK_SHIFT) + +/* Power bits */ +#define ADC3XXX_LADC_PWR_ON 0x80 +#define ADC3XXX_RADC_PWR_ON 0x40 + +#define ADC3XXX_SOFT_RESET 0x01 +#define ADC3XXX_BCLK_MASTER 0x08 +#define ADC3XXX_WCLK_MASTER 0x04 + +/* Interface register masks */ +#define ADC3XXX_FORMAT_MASK 0xc0 +#define ADC3XXX_FORMAT_SHIFT 6 +#define ADC3XXX_WLENGTH_MASK 0x30 +#define ADC3XXX_WLENGTH_SHIFT 4 +#define ADC3XXX_CLKDIR_MASK 0x0c +#define ADC3XXX_CLKDIR_SHIFT 2 + +/* Interface register bit patterns */ +#define ADC3XXX_FORMAT_I2S (0 << ADC3XXX_FORMAT_SHIFT) +#define ADC3XXX_FORMAT_DSP (1 << ADC3XXX_FORMAT_SHIFT) +#define ADC3XXX_FORMAT_RJF (2 << ADC3XXX_FORMAT_SHIFT) +#define ADC3XXX_FORMAT_LJF (3 << ADC3XXX_FORMAT_SHIFT) + +#define ADC3XXX_IFACE_16BITS (0 << ADC3XXX_WLENGTH_SHIFT) +#define ADC3XXX_IFACE_20BITS (1 << ADC3XXX_WLENGTH_SHIFT) +#define ADC3XXX_IFACE_24BITS (2 << ADC3XXX_WLENGTH_SHIFT) +#define ADC3XXX_IFACE_32BITS (3 << ADC3XXX_WLENGTH_SHIFT) + +/* PLL P/R bit offsets */ +#define ADC3XXX_PLLP_SHIFT 4 +#define ADC3XXX_PLLR_SHIFT 0 +#define ADC3XXX_PLL_PR_MASK 0x7f +#define ADC3XXX_PLLJ_MASK 0x3f +#define ADC3XXX_PLLD_MSB_MASK 0x3f +#define ADC3XXX_PLLD_LSB_MASK 0xff +#define ADC3XXX_NADC_MASK 0x7f +#define ADC3XXX_MADC_MASK 0x7f +#define ADC3XXX_AOSR_MASK 0xff +#define ADC3XXX_IADC_MASK 0xff +#define ADC3XXX_BDIV_MASK 0x7f + +/* PLL_CLKIN bits */ +#define ADC3XXX_PLL_CLKIN_SHIFT 2 +#define ADC3XXX_PLL_CLKIN_MCLK 0x0 +#define ADC3XXX_PLL_CLKIN_BCLK 0x1 +#define ADC3XXX_PLL_CLKIN_ZERO 0x3 + +/* CODEC_CLKIN bits */ +#define ADC3XXX_CODEC_CLKIN_SHIFT 0 +#define ADC3XXX_CODEC_CLKIN_MCLK 0x0 +#define ADC3XXX_CODEC_CLKIN_BCLK 0x1 +#define ADC3XXX_CODEC_CLKIN_PLL_CLK 0x3 + +#define ADC3XXX_USE_PLL ((ADC3XXX_PLL_CLKIN_MCLK << ADC3XXX_PLL_CLKIN_SHIFT) | \ + (ADC3XXX_CODEC_CLKIN_PLL_CLK << ADC3XXX_CODEC_CLKIN_SHIFT)) +#define ADC3XXX_NO_PLL ((ADC3XXX_PLL_CLKIN_ZERO << ADC3XXX_PLL_CLKIN_SHIFT) | \ + (ADC3XXX_CODEC_CLKIN_MCLK << ADC3XXX_CODEC_CLKIN_SHIFT)) + +/* Analog PGA control bits */ +#define ADC3XXX_LPGA_MUTE 0x80 +#define ADC3XXX_RPGA_MUTE 0x80 + +#define ADC3XXX_LPGA_GAIN_MASK 0x7f +#define ADC3XXX_RPGA_GAIN_MASK 0x7f + +/* ADC current modes */ +#define ADC3XXX_ADC_LOW_CURR_MODE 0x01 + +/* Left ADC Input selection bits */ +#define ADC3XXX_LCH_SEL1_SHIFT 0 +#define ADC3XXX_LCH_SEL2_SHIFT 2 +#define ADC3XXX_LCH_SEL3_SHIFT 4 +#define ADC3XXX_LCH_SEL4_SHIFT 6 + +#define ADC3XXX_LCH_SEL1X_SHIFT 0 +#define ADC3XXX_LCH_SEL2X_SHIFT 2 +#define ADC3XXX_LCH_SEL3X_SHIFT 4 +#define ADC3XXX_LCH_COMMON_MODE 0x40 +#define ADC3XXX_BYPASS_LPGA 0x80 + +/* Right ADC Input selection bits */ +#define ADC3XXX_RCH_SEL1_SHIFT 0 +#define ADC3XXX_RCH_SEL2_SHIFT 2 +#define ADC3XXX_RCH_SEL3_SHIFT 4 +#define ADC3XXX_RCH_SEL4_SHIFT 6 + +#define ADC3XXX_RCH_SEL1X_SHIFT 0 +#define ADC3XXX_RCH_SEL2X_SHIFT 2 +#define ADC3XXX_RCH_SEL3X_SHIFT 4 +#define ADC3XXX_RCH_COMMON_MODE 0x40 +#define ADC3XXX_BYPASS_RPGA 0x80 + +/* MICBIAS control bits */ +#define ADC3XXX_MICBIAS_MASK 0x2 +#define ADC3XXX_MICBIAS1_SHIFT 5 +#define ADC3XXX_MICBIAS2_SHIFT 3 + +#define ADC3XXX_ADC_MAX_VOLUME 64 +#define ADC3XXX_ADC_POS_VOL 24 + +/* GPIO control bits (GPIO1_CTRL and GPIO2_CTRL) */ +#define ADC3XXX_GPIO_CTRL_CFG_MASK 0x3c +#define ADC3XXX_GPIO_CTRL_CFG_SHIFT 2 +#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK 0x01 +#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT 0 +#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_MASK 0x02 +#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_SHIFT 1 + +enum adc3xxx_type { + ADC3001 = 0, + ADC3101 +}; + +struct adc3xxx { + struct device *dev; + enum adc3xxx_type type; + struct clk *mclk; + struct regmap *regmap; + struct gpio_desc *rst_pin; + unsigned int pll_mode; + unsigned int sysclk; + unsigned int gpio_cfg[ADC3XXX_GPIOS_MAX]; /* value+1 (0 => not set) */ + unsigned int micbias_vg[ADC3XXX_MICBIAS_PINS]; + int master; + u8 page_no; + int use_pll; + struct gpio_chip gpio_chip; +}; + +static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIOS_MAX] = { + ADC3XXX_GPIO1_CTRL, + ADC3XXX_GPIO2_CTRL +}; + +static const unsigned int adc3xxx_micbias_shift[ADC3XXX_MICBIAS_PINS] = { + ADC3XXX_MICBIAS1_SHIFT, + ADC3XXX_MICBIAS2_SHIFT +}; + +static const struct reg_default adc3xxx_defaults[] = { + /* Page 0 */ + { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, { 3, 0x00 }, + { 4, 0x00 }, { 5, 0x11 }, { 6, 0x04 }, { 7, 0x00 }, + { 8, 0x00 }, { 9, 0x00 }, { 10, 0x00 }, { 11, 0x00 }, + { 12, 0x00 }, { 13, 0x00 }, { 14, 0x00 }, { 15, 0x00 }, + { 16, 0x00 }, { 17, 0x00 }, { 18, 0x01 }, { 19, 0x01 }, + { 20, 0x80 }, { 21, 0x80 }, { 22, 0x04 }, { 23, 0x00 }, + { 24, 0x00 }, { 25, 0x00 }, { 26, 0x01 }, { 27, 0x00 }, + { 28, 0x00 }, { 29, 0x02 }, { 30, 0x01 }, { 31, 0x00 }, + { 32, 0x00 }, { 33, 0x10 }, { 34, 0x00 }, { 35, 0x00 }, + { 36, 0x00 }, { 37, 0x00 }, { 38, 0x02 }, { 39, 0x00 }, + { 40, 0x00 }, { 41, 0x00 }, { 42, 0x00 }, { 43, 0x00 }, + { 44, 0x00 }, { 45, 0x00 }, { 46, 0x00 }, { 47, 0x00 }, + { 48, 0x00 }, { 49, 0x00 }, { 50, 0x00 }, { 51, 0x00 }, + { 52, 0x00 }, { 53, 0x12 }, { 54, 0x00 }, { 55, 0x00 }, + { 56, 0x00 }, { 57, 0x00 }, { 58, 0x00 }, { 59, 0x44 }, + { 60, 0x00 }, { 61, 0x01 }, { 62, 0x00 }, { 63, 0x00 }, + { 64, 0x00 }, { 65, 0x00 }, { 66, 0x00 }, { 67, 0x00 }, + { 68, 0x00 }, { 69, 0x00 }, { 70, 0x00 }, { 71, 0x00 }, + { 72, 0x00 }, { 73, 0x00 }, { 74, 0x00 }, { 75, 0x00 }, + { 76, 0x00 }, { 77, 0x00 }, { 78, 0x00 }, { 79, 0x00 }, + { 80, 0x00 }, { 81, 0x00 }, { 82, 0x88 }, { 83, 0x00 }, + { 84, 0x00 }, { 85, 0x00 }, { 86, 0x00 }, { 87, 0x00 }, + { 88, 0x7f }, { 89, 0x00 }, { 90, 0x00 }, { 91, 0x00 }, + { 92, 0x00 }, { 93, 0x00 }, { 94, 0x00 }, { 95, 0x00 }, + { 96, 0x7f }, { 97, 0x00 }, { 98, 0x00 }, { 99, 0x00 }, + { 100, 0x00 }, { 101, 0x00 }, { 102, 0x00 }, { 103, 0x00 }, + { 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 }, + { 108, 0x00 }, { 109, 0x00 }, { 110, 0x00 }, { 111, 0x00 }, + { 112, 0x00 }, { 113, 0x00 }, { 114, 0x00 }, { 115, 0x00 }, + { 116, 0x00 }, { 117, 0x00 }, { 118, 0x00 }, { 119, 0x00 }, + { 120, 0x00 }, { 121, 0x00 }, { 122, 0x00 }, { 123, 0x00 }, + { 124, 0x00 }, { 125, 0x00 }, { 126, 0x00 }, { 127, 0x00 }, + + /* Page 1 */ + { 128, 0x00 }, { 129, 0x00 }, { 130, 0x00 }, { 131, 0x00 }, + { 132, 0x00 }, { 133, 0x00 }, { 134, 0x00 }, { 135, 0x00 }, + { 136, 0x00 }, { 137, 0x00 }, { 138, 0x00 }, { 139, 0x00 }, + { 140, 0x00 }, { 141, 0x00 }, { 142, 0x00 }, { 143, 0x00 }, + { 144, 0x00 }, { 145, 0x00 }, { 146, 0x00 }, { 147, 0x00 }, + { 148, 0x00 }, { 149, 0x00 }, { 150, 0x00 }, { 151, 0x00 }, + { 152, 0x00 }, { 153, 0x00 }, { 154, 0x00 }, { 155, 0x00 }, + { 156, 0x00 }, { 157, 0x00 }, { 158, 0x00 }, { 159, 0x00 }, + { 160, 0x00 }, { 161, 0x00 }, { 162, 0x00 }, { 163, 0x00 }, + { 164, 0x00 }, { 165, 0x00 }, { 166, 0x00 }, { 167, 0x00 }, + { 168, 0x00 }, { 169, 0x00 }, { 170, 0x00 }, { 171, 0x00 }, + { 172, 0x00 }, { 173, 0x00 }, { 174, 0x00 }, { 175, 0x00 }, + { 176, 0x00 }, { 177, 0x00 }, { 178, 0x00 }, { 179, 0x00 }, + { 180, 0xff }, { 181, 0x00 }, { 182, 0x3f }, { 183, 0xff }, + { 184, 0x00 }, { 185, 0x3f }, { 186, 0x00 }, { 187, 0x80 }, + { 188, 0x80 }, { 189, 0x00 }, { 190, 0x00 }, { 191, 0x00 }, +}; + +static bool adc3xxx_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADC3XXX_RESET: + return true; + default: + return false; + } +} + +static const struct regmap_range_cfg adc3xxx_ranges[] = { + { + .range_min = 0, + .range_max = 2 * ADC3XXX_PAGE_SIZE, + .selector_reg = ADC3XXX_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = ADC3XXX_PAGE_SIZE, + } +}; + +static const struct regmap_config adc3xxx_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = adc3xxx_defaults, + .num_reg_defaults = ARRAY_SIZE(adc3xxx_defaults), + + .volatile_reg = adc3xxx_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + + .ranges = adc3xxx_ranges, + .num_ranges = ARRAY_SIZE(adc3xxx_ranges), + .max_register = 2 * ADC3XXX_PAGE_SIZE, +}; + +struct adc3xxx_rate_divs { + u32 mclk; + u32 rate; + u8 pll_p; + u8 pll_r; + u8 pll_j; + u16 pll_d; + u8 nadc; + u8 madc; + u8 aosr; +}; + +/* + * PLL and Clock settings. + * If p member is 0, PLL is not used. + * The order of the entries in this table have the PLL entries before + * the non-PLL entries, so that the PLL modes are preferred unless + * the PLL mode setting says otherwise. + */ +static const struct adc3xxx_rate_divs adc3xxx_divs[] = { + /* mclk, rate, p, r, j, d, nadc, madc, aosr */ + /* 8k rate */ + { 12000000, 8000, 1, 1, 7, 1680, 42, 2, 128 }, + { 12288000, 8000, 1, 1, 7, 0000, 42, 2, 128 }, + /* 11.025k rate */ + { 12000000, 11025, 1, 1, 6, 8208, 29, 2, 128 }, + /* 16k rate */ + { 12000000, 16000, 1, 1, 7, 1680, 21, 2, 128 }, + { 12288000, 16000, 1, 1, 7, 0000, 21, 2, 128 }, + /* 22.05k rate */ + { 12000000, 22050, 1, 1, 7, 560, 15, 2, 128 }, + /* 32k rate */ + { 12000000, 32000, 1, 1, 8, 1920, 12, 2, 128 }, + { 12288000, 32000, 1, 1, 8, 0000, 12, 2, 128 }, + /* 44.1k rate */ + { 12000000, 44100, 1, 1, 7, 5264, 8, 2, 128 }, + /* 48k rate */ + { 12000000, 48000, 1, 1, 7, 1680, 7, 2, 128 }, + { 12288000, 48000, 1, 1, 7, 0000, 7, 2, 128 }, + { 24576000, 48000, 1, 1, 3, 5000, 7, 2, 128 }, /* With PLL */ + { 24576000, 48000, 0, 0, 0, 0000, 2, 2, 128 }, /* Without PLL */ + /* 88.2k rate */ + { 12000000, 88200, 1, 1, 7, 5264, 4, 4, 64 }, + /* 96k rate */ + { 12000000, 96000, 1, 1, 8, 1920, 4, 4, 64 }, +}; + +static int adc3xxx_get_divs(struct device *dev, int mclk, int rate, int pll_mode) +{ + int i; + + dev_dbg(dev, "mclk = %d, rate = %d, clock mode %u\n", + mclk, rate, pll_mode); + for (i = 0; i < ARRAY_SIZE(adc3xxx_divs); i++) { + const struct adc3xxx_rate_divs *mode = &adc3xxx_divs[i]; + + /* Skip this entry if it doesn't fulfill the intended clock + * mode requirement. We consider anything besides the two + * modes below to be the same as ADC3XXX_PLL_AUTO. + */ + if ((pll_mode == ADC3XXX_PLL_BYPASS && mode->pll_p) || + (pll_mode == ADC3XXX_PLL_ENABLE && !mode->pll_p)) + continue; + + if (mode->rate == rate && mode->mclk == mclk) + return i; + } + + dev_info(dev, "Master clock rate %d and sample rate %d is not supported\n", + mclk, rate); + return -EINVAL; +} + +static int adc3xxx_pll_delay(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + /* 10msec delay needed after PLL power-up to allow + * PLL and dividers to stabilize (datasheet p13). + */ + usleep_range(10000, 20000); + + return 0; +} + +static const char * const adc_softstepping_text[] = { "1 step", "2 step", "off" }; +static SOC_ENUM_SINGLE_DECL(adc_softstepping_enum, ADC3XXX_ADC_DIGITAL, 0, + adc_softstepping_text); + +static const char * const multiplier_text[] = { "1", "2", "4", "8", "16", "32", "64", "128" }; +static SOC_ENUM_SINGLE_DECL(left_agc_attack_mult_enum, + ADC3XXX_LEFT_CHN_AGC_4, 0, multiplier_text); +static SOC_ENUM_SINGLE_DECL(right_agc_attack_mult_enum, + ADC3XXX_RIGHT_CHN_AGC_4, 0, multiplier_text); +static SOC_ENUM_SINGLE_DECL(left_agc_decay_mult_enum, + ADC3XXX_LEFT_CHN_AGC_5, 0, multiplier_text); +static SOC_ENUM_SINGLE_DECL(right_agc_decay_mult_enum, + ADC3XXX_RIGHT_CHN_AGC_5, 0, multiplier_text); + +static const char * const dither_dc_offset_text[] = { + "0mV", "15mV", "30mV", "45mV", "60mV", "75mV", "90mV", "105mV", + "-15mV", "-30mV", "-45mV", "-60mV", "-75mV", "-90mV", "-105mV" +}; +static const unsigned int dither_dc_offset_values[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15 +}; +static SOC_VALUE_ENUM_DOUBLE_DECL(dither_dc_offset_enum, + ADC3XXX_DITHER_CTRL, + 4, 0, 0xf, dither_dc_offset_text, + dither_dc_offset_values); + +static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_fine_tlv, -40, 10, 0); +/* AGC target: 8 values: -5.5, -8, -10, -12, -14, -17, -20, -24 dB */ +/* It would be nice to declare these in the order above, but empirically + * TLV_DB_SCALE_ITEM doesn't take lightly to the increment (second) parameter + * being negative, despite there being examples to the contrary in other + * drivers. So declare these in the order from lowest to highest, and + * set the invert flag in the SOC_DOUBLE_R_TLV declaration instead. + */ +static const DECLARE_TLV_DB_RANGE(agc_target_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-2400, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(-2000, 300, 0), + 4, 6, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 7, 7, TLV_DB_SCALE_ITEM(-550, 0, 0)); +/* Since the 'disabled' value (mute) is at the highest value in the dB + * range (i.e. just before -32 dB) rather than the lowest, we need to resort + * to using a TLV_DB_RANGE in order to get the mute value in the right place. + */ +static const DECLARE_TLV_DB_RANGE(agc_thresh_tlv, + 0, 30, TLV_DB_SCALE_ITEM(-9000, 200, 0), + 31, 31, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */ +/* AGC hysteresis: 4 values: 1, 2, 4 dB, disabled (= mute) */ +static const DECLARE_TLV_DB_RANGE(agc_hysteresis_tlv, + 0, 1, TLV_DB_SCALE_ITEM(100, 100, 0), + 2, 2, TLV_DB_SCALE_ITEM(400, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */ +static const DECLARE_TLV_DB_SCALE(agc_max_tlv, 0, 50, 0); +/* Input attenuation: -6 dB or 0 dB */ +static const DECLARE_TLV_DB_SCALE(input_attenuation_tlv, -600, 600, 0); + +static const struct snd_kcontrol_new adc3xxx_snd_controls[] = { + SOC_DOUBLE_R_TLV("PGA Capture Volume", ADC3XXX_LEFT_APGA_CTRL, + ADC3XXX_RIGHT_APGA_CTRL, 0, 80, 0, pga_tlv), + SOC_DOUBLE("PGA Capture Switch", ADC3XXX_ADC_FGA, 7, 3, 1, 1), + SOC_DOUBLE_R("AGC Capture Switch", ADC3XXX_LEFT_CHN_AGC_1, + ADC3XXX_RIGHT_CHN_AGC_1, 7, 1, 0), + SOC_DOUBLE_R_TLV("AGC Target Level Capture Volume", ADC3XXX_LEFT_CHN_AGC_1, + ADC3XXX_RIGHT_CHN_AGC_2, 4, 0x07, 1, agc_target_tlv), + SOC_DOUBLE_R_TLV("AGC Noise Threshold Capture Volume", ADC3XXX_LEFT_CHN_AGC_2, + ADC3XXX_RIGHT_CHN_AGC_2, 1, 0x1f, 1, agc_thresh_tlv), + SOC_DOUBLE_R_TLV("AGC Hysteresis Capture Volume", ADC3XXX_LEFT_CHN_AGC_2, + ADC3XXX_RIGHT_CHN_AGC_2, 6, 3, 0, agc_hysteresis_tlv), + SOC_DOUBLE_R("AGC Clip Stepping Capture Switch", ADC3XXX_LEFT_CHN_AGC_2, + ADC3XXX_RIGHT_CHN_AGC_2, 0, 1, 0), + /* + * Oddly enough, the data sheet says the default value + * for the left/right AGC maximum gain register field + * (ADC3XXX_LEFT/RIGHT_CHN_AGC_3 bits 0..6) is 0x7f = 127 + * (verified empirically) even though this value (indeed, above + * 0x50) is specified as 'Reserved. Do not use.' in the accompanying + * table in the data sheet. + */ + SOC_DOUBLE_R_TLV("AGC Maximum Capture Volume", ADC3XXX_LEFT_CHN_AGC_3, + ADC3XXX_RIGHT_CHN_AGC_3, 0, 0x50, 0, agc_max_tlv), + SOC_DOUBLE_R("AGC Attack Time", ADC3XXX_LEFT_CHN_AGC_4, + ADC3XXX_RIGHT_CHN_AGC_4, 3, 0x1f, 0), + /* Would like to have the multipliers as LR pairs, but there is + * no SOC_ENUM_foo which accepts two values in separate registers. + */ + SOC_ENUM("AGC Left Attack Time Multiplier", left_agc_attack_mult_enum), + SOC_ENUM("AGC Right Attack Time Multiplier", right_agc_attack_mult_enum), + SOC_DOUBLE_R("AGC Decay Time", ADC3XXX_LEFT_CHN_AGC_5, + ADC3XXX_RIGHT_CHN_AGC_5, 3, 0x1f, 0), + SOC_ENUM("AGC Left Decay Time Multiplier", left_agc_decay_mult_enum), + SOC_ENUM("AGC Right Decay Time Multiplier", right_agc_decay_mult_enum), + SOC_DOUBLE_R("AGC Noise Debounce", ADC3XXX_LEFT_CHN_AGC_6, + ADC3XXX_RIGHT_CHN_AGC_6, 0, 0x1f, 0), + SOC_DOUBLE_R("AGC Signal Debounce", ADC3XXX_LEFT_CHN_AGC_7, + ADC3XXX_RIGHT_CHN_AGC_7, 0, 0x0f, 0), + /* Read only register */ + SOC_DOUBLE_R_S_TLV("AGC Applied Capture Volume", ADC3XXX_LEFT_AGC_GAIN, + ADC3XXX_RIGHT_AGC_GAIN, 0, -24, 40, 6, 0, adc_tlv), + /* ADC soft stepping */ + SOC_ENUM("ADC Soft Stepping", adc_softstepping_enum), + /* Left/Right Input attenuation */ + SOC_SINGLE_TLV("Left Input IN_1L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input IN_2L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input IN_3L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input IN_1R Capture Volume", + ADC3XXX_LEFT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input DIF_2L_3L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input DIF_1L_1R Capture Volume", + ADC3XXX_LEFT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input DIF_2R_3R Capture Volume", + ADC3XXX_LEFT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_1R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_2R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_3R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_1L Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input DIF_2R_3R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input DIF_1L_1R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input DIF_2L_3L Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv), + SOC_DOUBLE_R_S_TLV("ADC Volume Control Capture Volume", ADC3XXX_LADC_VOL, + ADC3XXX_RADC_VOL, 0, -24, 40, 6, 0, adc_tlv), + /* Empirically, the following doesn't work the way it's supposed + * to. Values 0, -0.1, -0.2 and -0.3 dB result in the same level, and + * -0.4 dB drops about 0.12 dB on a specific chip. + */ + SOC_DOUBLE_TLV("ADC Fine Volume Control Capture Volume", ADC3XXX_ADC_FGA, + 4, 0, 4, 1, adc_fine_tlv), + SOC_SINGLE("Left ADC Unselected CM Bias Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 6, 1, 0), + SOC_SINGLE("Right ADC Unselected CM Bias Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 6, 1, 0), + SOC_ENUM("Dither Control DC Offset", dither_dc_offset_enum), +}; + +/* Left input selection, Single Ended inputs and Differential inputs */ +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN_1L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 1, 0x1, 1), + SOC_DAPM_SINGLE("IN_2L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_3L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 7, 0x1, 1), + SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_1R Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 1, 0x1, 1), +}; + +/* Right input selection, Single Ended inputs and Differential inputs */ +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN_1R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 1, 0x1, 1), + SOC_DAPM_SINGLE("IN_2R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_3R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 7, 0x1, 1), + SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_1L Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 1, 0x1, 1), +}; + +/* Left Digital Mic input for left ADC */ +static const struct snd_kcontrol_new left_input_dmic_controls[] = { + SOC_DAPM_SINGLE("Left ADC Capture Switch", + ADC3XXX_ADC_DIGITAL, 3, 0x1, 0), +}; + +/* Right Digital Mic input for Right ADC */ +static const struct snd_kcontrol_new right_input_dmic_controls[] = { + SOC_DAPM_SINGLE("Right ADC Capture Switch", + ADC3XXX_ADC_DIGITAL, 2, 0x1, 0), +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget adc3xxx_dapm_widgets[] = { + + /* Left Input Selection */ + SND_SOC_DAPM_MIXER("Left Input", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + /* Right Input Selection */ + SND_SOC_DAPM_MIXER("Right Input", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + /* PGA selection */ + SND_SOC_DAPM_PGA("Left PGA", ADC3XXX_LEFT_APGA_CTRL, 7, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right PGA", ADC3XXX_RIGHT_APGA_CTRL, 7, 1, NULL, 0), + + /* Digital Microphone Input Control for Left/Right ADC */ + SND_SOC_DAPM_MIXER("Left DMic Input", SND_SOC_NOPM, 0, 0, + &left_input_dmic_controls[0], + ARRAY_SIZE(left_input_dmic_controls)), + SND_SOC_DAPM_MIXER("Right DMic Input", SND_SOC_NOPM, 0, 0, + &right_input_dmic_controls[0], + ARRAY_SIZE(right_input_dmic_controls)), + + /* Left/Right ADC */ + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ADC3XXX_ADC_DIGITAL, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ADC3XXX_ADC_DIGITAL, 6, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("IN_1L"), + SND_SOC_DAPM_INPUT("IN_1R"), + SND_SOC_DAPM_INPUT("IN_2L"), + SND_SOC_DAPM_INPUT("IN_2R"), + SND_SOC_DAPM_INPUT("IN_3L"), + SND_SOC_DAPM_INPUT("IN_3R"), + SND_SOC_DAPM_INPUT("DIFL_1L_1R"), + SND_SOC_DAPM_INPUT("DIFL_2L_3L"), + SND_SOC_DAPM_INPUT("DIFL_2R_3R"), + SND_SOC_DAPM_INPUT("DIFR_1L_1R"), + SND_SOC_DAPM_INPUT("DIFR_2L_3L"), + SND_SOC_DAPM_INPUT("DIFR_2R_3R"), + SND_SOC_DAPM_INPUT("DMic_L"), + SND_SOC_DAPM_INPUT("DMic_R"), + + /* Digital audio interface output */ + SND_SOC_DAPM_AIF_OUT("AIF_OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Clocks */ + SND_SOC_DAPM_SUPPLY("PLL_CLK", ADC3XXX_PLL_PROG_PR, ADC3XXX_ENABLE_PLL_SHIFT, + 0, adc3xxx_pll_delay, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("ADC_CLK", ADC3XXX_ADC_NADC, ADC3XXX_ENABLE_NADC_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_MOD_CLK", ADC3XXX_ADC_MADC, ADC3XXX_ENABLE_MADC_SHIFT, + 0, NULL, 0), + + /* This refers to the generated BCLK in master mode. */ + SND_SOC_DAPM_SUPPLY("BCLK", ADC3XXX_BCLK_N_DIV, ADC3XXX_ENABLE_BCLK_SHIFT, + 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route adc3xxx_intercon[] = { + /* Left input selection from switches */ + { "Left Input", "IN_1L Capture Switch", "IN_1L" }, + { "Left Input", "IN_2L Capture Switch", "IN_2L" }, + { "Left Input", "IN_3L Capture Switch", "IN_3L" }, + { "Left Input", "DIF_2L_3L Capture Switch", "DIFL_2L_3L" }, + { "Left Input", "DIF_1L_1R Capture Switch", "DIFL_1L_1R" }, + { "Left Input", "DIF_2R_3R Capture Switch", "DIFL_2R_3R" }, + { "Left Input", "IN_1R Capture Switch", "IN_1R" }, + + /* Left input selection to left PGA */ + { "Left PGA", NULL, "Left Input" }, + + /* Left PGA to left ADC */ + { "Left ADC", NULL, "Left PGA" }, + + /* Right input selection from switches */ + { "Right Input", "IN_1R Capture Switch", "IN_1R" }, + { "Right Input", "IN_2R Capture Switch", "IN_2R" }, + { "Right Input", "IN_3R Capture Switch", "IN_3R" }, + { "Right Input", "DIF_2R_3R Capture Switch", "DIFR_2R_3R" }, + { "Right Input", "DIF_1L_1R Capture Switch", "DIFR_1L_1R" }, + { "Right Input", "DIF_2L_3L Capture Switch", "DIFR_2L_3L" }, + { "Right Input", "IN_1L Capture Switch", "IN_1L" }, + + /* Right input selection to right PGA */ + { "Right PGA", NULL, "Right Input" }, + + /* Right PGA to right ADC */ + { "Right ADC", NULL, "Right PGA" }, + + /* Left DMic Input selection from switch */ + { "Left DMic Input", "Left ADC Capture Switch", "DMic_L" }, + + /* Left DMic to left ADC */ + { "Left ADC", NULL, "Left DMic Input" }, + + /* Right DMic Input selection from switch */ + { "Right DMic Input", "Right ADC Capture Switch", "DMic_R" }, + + /* Right DMic to right ADC */ + { "Right ADC", NULL, "Right DMic Input" }, + + /* ADC to AIF output */ + { "AIF_OUT", NULL, "Left ADC" }, + { "AIF_OUT", NULL, "Right ADC" }, + + /* Clocking */ + { "ADC_MOD_CLK", NULL, "ADC_CLK" }, + { "Left ADC", NULL, "ADC_MOD_CLK" }, + { "Right ADC", NULL, "ADC_MOD_CLK" }, + + { "BCLK", NULL, "ADC_CLK" }, +}; + +static const struct snd_soc_dapm_route adc3xxx_pll_intercon[] = { + { "ADC_CLK", NULL, "PLL_CLK" }, +}; + +static const struct snd_soc_dapm_route adc3xxx_bclk_out_intercon[] = { + { "AIF_OUT", NULL, "BCLK" } +}; + +static int adc3xxx_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + + if (offset >= ADC3XXX_GPIOS_MAX) + return -EINVAL; + + /* GPIO1 is offset 0, GPIO2 is offset 1 */ + /* We check here that the GPIO pins are either not configured in the + * DT, or that they purposely are set as outputs. + * (Input mode not yet implemented). + */ + if (adc3xxx->gpio_cfg[offset] != 0 && + adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1) + return -EINVAL; + + return 0; +} + +static int adc3xxx_gpio_direction_out(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + + /* Set GPIO output function. */ + return regmap_update_bits(adc3xxx->regmap, + adc3xxx_gpio_ctrl_reg[offset], + ADC3XXX_GPIO_CTRL_CFG_MASK | + ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK, + ADC3XXX_GPIO_GPO << ADC3XXX_GPIO_CTRL_CFG_SHIFT | + !!value << ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT); +} + +/* With only GPIO outputs configured, we never get the .direction_out call, + * so we set the output mode and output value in the same call. Hence + * .set in practice does the same thing as .direction_out . + */ +static void adc3xxx_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + (void) adc3xxx_gpio_direction_out(chip, offset, value); +} + +/* Even though we only support GPIO output for now, some GPIO clients + * want to read the current pin state using the .get callback. + */ +static int adc3xxx_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + unsigned int regval; + int ret; + + /* We only allow output pins, so just read the value set in the output + * pin register field. + */ + ret = regmap_read(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], ®val); + if (ret) + return ret; + return !!(regval & ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK); +} + +static const struct gpio_chip adc3xxx_gpio_chip = { + .label = "adc3xxx", + .owner = THIS_MODULE, + .request = adc3xxx_gpio_request, + .direction_output = adc3xxx_gpio_direction_out, + .set = adc3xxx_gpio_set, + .get = adc3xxx_gpio_get, + .can_sleep = 1, +}; + +static void adc3xxx_free_gpio(struct adc3xxx *adc3xxx) +{ + gpiochip_remove(&adc3xxx->gpio_chip); +} + +static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) +{ + int gpio, micbias; + int ret; + + adc3xxx->gpio_chip = adc3xxx_gpio_chip; + adc3xxx->gpio_chip.ngpio = ADC3XXX_GPIOS_MAX; + adc3xxx->gpio_chip.parent = adc3xxx->dev; + adc3xxx->gpio_chip.base = -1; + + ret = gpiochip_add_data(&adc3xxx->gpio_chip, adc3xxx); + if (ret) + dev_err(adc3xxx->dev, "Failed to add gpios: %d\n", ret); + + /* Set up potential GPIO configuration from the devicetree. + * This allows us to set up things which are not software + * controllable GPIOs, such as PDM microphone I/O, + */ + for (gpio = 0; gpio < ADC3XXX_GPIOS_MAX; gpio++) { + unsigned int cfg = adc3xxx->gpio_cfg[gpio]; + + if (cfg) { + cfg--; /* actual value to use is stored +1 */ + regmap_update_bits(adc3xxx->regmap, + adc3xxx_gpio_ctrl_reg[gpio], + ADC3XXX_GPIO_CTRL_CFG_MASK, + cfg << ADC3XXX_GPIO_CTRL_CFG_SHIFT); + } + } + + /* Set up micbias voltage */ + for (micbias = 0; micbias < ADC3XXX_MICBIAS_PINS; micbias++) { + unsigned int vg = adc3xxx->micbias_vg[micbias]; + + regmap_update_bits(adc3xxx->regmap, + ADC3XXX_MICBIAS_CTRL, + ADC3XXX_MICBIAS_MASK << adc3xxx_micbias_shift[micbias], + vg << adc3xxx_micbias_shift[micbias]); + } +} + +static int adc3xxx_parse_dt_gpio(struct adc3xxx *adc3xxx, + const char *propname, unsigned int *cfg) +{ + struct device *dev = adc3xxx->dev; + struct device_node *np = dev->of_node; + unsigned int val; + + if (!of_property_read_u32(np, propname, &val)) { + if (val & ~15 || val == 7 || val >= 11) { + dev_err(dev, "Invalid property value for '%s'\n", propname); + return -EINVAL; + } + if (val == ADC3XXX_GPIO_GPI) + dev_warn(dev, "GPIO Input read not yet implemented\n"); + *cfg = val + 1; /* 0 => not set up, all others shifted +1 */ + } + return 0; +} + +static int adc3xxx_parse_dt_micbias(struct adc3xxx *adc3xxx, + const char *propname, unsigned int *vg) +{ + struct device *dev = adc3xxx->dev; + struct device_node *np = dev->of_node; + unsigned int val; + + if (!of_property_read_u32(np, propname, &val)) { + if (val >= ADC3XXX_MICBIAS_AVDD) { + dev_err(dev, "Invalid property value for '%s'\n", propname); + return -EINVAL; + } + *vg = val; + } + return 0; +} + +static int adc3xxx_parse_pll_mode(uint32_t val, unsigned int *pll_mode) +{ + if (val != ADC3XXX_PLL_ENABLE && val != ADC3XXX_PLL_BYPASS && + val != ADC3XXX_PLL_AUTO) + return -EINVAL; + + *pll_mode = val; + + return 0; +} + +static void adc3xxx_setup_pll(struct snd_soc_component *component, + int div_entry) +{ + int i = div_entry; + + /* P & R values */ + snd_soc_component_write(component, ADC3XXX_PLL_PROG_PR, + (adc3xxx_divs[i].pll_p << ADC3XXX_PLLP_SHIFT) | + (adc3xxx_divs[i].pll_r << ADC3XXX_PLLR_SHIFT)); + /* J value */ + snd_soc_component_write(component, ADC3XXX_PLL_PROG_J, + adc3xxx_divs[i].pll_j & ADC3XXX_PLLJ_MASK); + /* D value */ + snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_LSB, + adc3xxx_divs[i].pll_d & ADC3XXX_PLLD_LSB_MASK); + snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_MSB, + (adc3xxx_divs[i].pll_d >> 8) & ADC3XXX_PLLD_MSB_MASK); +} + +static int adc3xxx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(dai->component); + struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); + int i, width = 16; + u8 iface_len, bdiv; + + i = adc3xxx_get_divs(component->dev, adc3xxx->sysclk, + params_rate(params), adc3xxx->pll_mode); + + if (i < 0) + return i; + + /* select data word length */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + iface_len = ADC3XXX_IFACE_16BITS; + width = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface_len = ADC3XXX_IFACE_20BITS; + width = 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface_len = ADC3XXX_IFACE_24BITS; + width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface_len = ADC3XXX_IFACE_32BITS; + width = 32; + break; + default: + dev_err(component->dev, "Unsupported serial data format\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, ADC3XXX_INTERFACE_CTRL_1, + ADC3XXX_WLENGTH_MASK, iface_len); + if (adc3xxx_divs[i].pll_p) { /* If PLL used for this mode */ + adc3xxx_setup_pll(component, i); + snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_USE_PLL); + if (!adc3xxx->use_pll) { + snd_soc_dapm_add_routes(dapm, adc3xxx_pll_intercon, + ARRAY_SIZE(adc3xxx_pll_intercon)); + adc3xxx->use_pll = 1; + } + } else { + snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_NO_PLL); + if (adc3xxx->use_pll) { + snd_soc_dapm_del_routes(dapm, adc3xxx_pll_intercon, + ARRAY_SIZE(adc3xxx_pll_intercon)); + adc3xxx->use_pll = 0; + } + } + + /* NADC */ + snd_soc_component_update_bits(component, ADC3XXX_ADC_NADC, + ADC3XXX_NADC_MASK, adc3xxx_divs[i].nadc); + /* MADC */ + snd_soc_component_update_bits(component, ADC3XXX_ADC_MADC, + ADC3XXX_MADC_MASK, adc3xxx_divs[i].madc); + /* AOSR */ + snd_soc_component_update_bits(component, ADC3XXX_ADC_AOSR, + ADC3XXX_AOSR_MASK, adc3xxx_divs[i].aosr); + /* BDIV N Value */ + /* BCLK is (by default) set up to be derived from ADC_CLK */ + bdiv = (adc3xxx_divs[i].aosr * adc3xxx_divs[i].madc) / (2 * width); + snd_soc_component_update_bits(component, ADC3XXX_BCLK_N_DIV, + ADC3XXX_BDIV_MASK, bdiv); + + return 0; +} + +static const char *adc3xxx_pll_mode_text(int pll_mode) +{ + switch (pll_mode) { + case ADC3XXX_PLL_AUTO: + return "PLL auto"; + case ADC3XXX_PLL_ENABLE: + return "PLL enable"; + case ADC3XXX_PLL_BYPASS: + return "PLL bypass"; + default: + break; + } + + return "PLL unknown"; +} + +static int adc3xxx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); + int ret; + + ret = adc3xxx_parse_pll_mode(clk_id, &adc3xxx->pll_mode); + if (ret < 0) + return ret; + + adc3xxx->sysclk = freq; + dev_dbg(component->dev, "Set sysclk to %u Hz, %s\n", + freq, adc3xxx_pll_mode_text(adc3xxx->pll_mode)); + return 0; +} + +static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); + u8 clkdir = 0, format = 0; + int master = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + master = 1; + clkdir = ADC3XXX_BCLK_MASTER | ADC3XXX_WCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + master = 0; + break; + default: + dev_err(component->dev, "Invalid DAI clock setup\n"); + return -EINVAL; + } + + /* + * match both interface format and signal polarities since they + * are fixed + */ + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK)) { + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF: + format = ADC3XXX_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF: + format = ADC3XXX_FORMAT_DSP; + break; + case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF: + format = ADC3XXX_FORMAT_DSP; + break; + case SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF: + format = ADC3XXX_FORMAT_RJF; + break; + case SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF: + format = ADC3XXX_FORMAT_LJF; + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + /* Add/del route enabling BCLK output as applicable */ + if (master && !adc3xxx->master) + snd_soc_dapm_add_routes(dapm, adc3xxx_bclk_out_intercon, + ARRAY_SIZE(adc3xxx_bclk_out_intercon)); + else if (!master && adc3xxx->master) + snd_soc_dapm_del_routes(dapm, adc3xxx_bclk_out_intercon, + ARRAY_SIZE(adc3xxx_bclk_out_intercon)); + adc3xxx->master = master; + + /* set clock direction and format */ + return snd_soc_component_update_bits(component, + ADC3XXX_INTERFACE_CTRL_1, + ADC3XXX_CLKDIR_MASK | ADC3XXX_FORMAT_MASK, + clkdir | format); +} + +static const struct snd_soc_dai_ops adc3xxx_dai_ops = { + .hw_params = adc3xxx_hw_params, + .set_sysclk = adc3xxx_set_dai_sysclk, + .set_fmt = adc3xxx_set_dai_fmt, +}; + +static struct snd_soc_dai_driver adc3xxx_dai = { + .name = "tlv320adc3xxx-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ADC3XXX_RATES, + .formats = ADC3XXX_FORMATS, + }, + .ops = &adc3xxx_dai_ops, +}; + +static const struct snd_soc_component_driver soc_component_dev_adc3xxx = { + .controls = adc3xxx_snd_controls, + .num_controls = ARRAY_SIZE(adc3xxx_snd_controls), + .dapm_widgets = adc3xxx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adc3xxx_dapm_widgets), + .dapm_routes = adc3xxx_intercon, + .num_dapm_routes = ARRAY_SIZE(adc3xxx_intercon), +}; + +static int adc3xxx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct adc3xxx *adc3xxx = NULL; + int ret; + + adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL); + if (!adc3xxx) + return -ENOMEM; + adc3xxx->dev = dev; + + adc3xxx->rst_pin = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(adc3xxx->rst_pin)) { + return dev_err_probe(dev, PTR_ERR(adc3xxx->rst_pin), + "Failed to request rst_pin\n"); + } + + adc3xxx->mclk = devm_clk_get(dev, NULL); + if (IS_ERR(adc3xxx->mclk)) { + /* + * The chip itself supports running off the BCLK either + * directly or via the PLL, but the driver does not (yet), so + * having a specified mclk is required. Otherwise, we could + * use the lack of a clocks property to indicate when BCLK is + * intended as the clock source. + */ + return dev_err_probe(dev, PTR_ERR(adc3xxx->mclk), + "Failed to acquire MCLK\n"); + } else if (adc3xxx->mclk) { + ret = clk_prepare_enable(adc3xxx->mclk); + if (ret < 0) + return ret; + dev_dbg(dev, "Enabled MCLK, freq %lu Hz\n", clk_get_rate(adc3xxx->mclk)); + } + + ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmdin-gpio1", &adc3xxx->gpio_cfg[0]); + if (ret < 0) + goto err_unprepare_mclk; + ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmclk-gpio2", &adc3xxx->gpio_cfg[1]); + if (ret < 0) + goto err_unprepare_mclk; + ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); + if (ret < 0) + goto err_unprepare_mclk; + ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); + if (ret < 0) + goto err_unprepare_mclk; + + adc3xxx->regmap = devm_regmap_init_i2c(i2c, &adc3xxx_regmap); + if (IS_ERR(adc3xxx->regmap)) { + ret = PTR_ERR(adc3xxx->regmap); + goto err_unprepare_mclk; + } + + i2c_set_clientdata(i2c, adc3xxx); + + adc3xxx->type = id->driver_data; + + /* Reset codec chip */ + gpiod_set_value_cansleep(adc3xxx->rst_pin, 1); + usleep_range(2000, 100000); /* Requirement: > 10 ns (datasheet p13) */ + gpiod_set_value_cansleep(adc3xxx->rst_pin, 0); + + /* Potentially set up pins used as GPIOs */ + adc3xxx_init_gpio(adc3xxx); + + ret = snd_soc_register_component(dev, + &soc_component_dev_adc3xxx, &adc3xxx_dai, 1); + if (ret < 0) { + dev_err(dev, "Failed to register codec: %d\n", ret); + goto err_unprepare_mclk; + } + + return 0; + +err_unprepare_mclk: + clk_disable_unprepare(adc3xxx->mclk); + return ret; +} + +static int __exit adc3xxx_i2c_remove(struct i2c_client *client) +{ + struct adc3xxx *adc3xxx = i2c_get_clientdata(client); + + if (adc3xxx->mclk) + clk_disable_unprepare(adc3xxx->mclk); + adc3xxx_free_gpio(adc3xxx); + snd_soc_unregister_component(&client->dev); + return 0; +} + +static const struct of_device_id tlv320adc3xxx_of_match[] = { + { .compatible = "ti,tlv320adc3001", }, + { .compatible = "ti,tlv320adc3101", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tlv320adc3xxx_of_match); + +static const struct i2c_device_id adc3xxx_i2c_id[] = { + { "tlv320adc3001", ADC3001 }, + { "tlv320adc3101", ADC3101 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id); + +static struct i2c_driver adc3xxx_i2c_driver = { + .driver = { + .name = "tlv320adc3xxx-codec", + .of_match_table = tlv320adc3xxx_of_match, + }, + .probe = adc3xxx_i2c_probe, + .remove = adc3xxx_i2c_remove, + .id_table = adc3xxx_i2c_id, +}; + +module_i2c_driver(adc3xxx_i2c_driver); + +MODULE_DESCRIPTION("ASoC TLV320ADC3xxx codec driver"); +MODULE_AUTHOR("shahina.s@mistralsolutions.com"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h index 1f2879b7a080..c86569883e0c 100644 --- a/sound/soc/codecs/tlv320aic26.h +++ b/sound/soc/codecs/tlv320aic26.h @@ -6,8 +6,8 @@ * Copyright (C) 2008 Secret Lab Technologies Ltd. */ -#ifndef _TLV320AIC16_H_ -#define _TLV320AIC16_H_ +#ifndef _TLV320AIC26_H_ +#define _TLV320AIC26_H_ /* AIC26 Registers */ #define AIC26_PAGE_ADDR(page, offset) ((page << 11) | offset << 5) @@ -88,4 +88,4 @@ enum aic26_wlen { AIC26_WLEN_32 = 3 << 10, }; -#endif /* _TLV320AIC16_H_ */ +#endif /* _TLV320AIC26_H_ */ diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 52d2c968b5c0..e77342aff46d 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> @@ -169,6 +170,7 @@ struct aic31xx_priv { struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES]; struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES]; struct snd_soc_jack *jack; + u32 sysclk_id; unsigned int sysclk; u8 p_div; int rate_div_line; @@ -180,6 +182,7 @@ struct aic31xx_priv { struct aic31xx_rate_divs { u32 mclk_p; u32 rate; + u8 pll_r; u8 pll_j; u16 pll_d; u16 dosr; @@ -192,51 +195,71 @@ struct aic31xx_rate_divs { /* ADC dividers can be disabled by configuring them to 0 */ static const struct aic31xx_rate_divs aic31xx_divs[] = { - /* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */ + /* mclk/p rate pll: r j d dosr ndac mdac aors nadc madc */ /* 8k rate */ - {12000000, 8000, 8, 1920, 128, 48, 2, 128, 48, 2}, - {12000000, 8000, 8, 1920, 128, 32, 3, 128, 32, 3}, - {12500000, 8000, 7, 8643, 128, 48, 2, 128, 48, 2}, + { 512000, 8000, 4, 48, 0, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 1, 8, 1920, 128, 32, 3, 128, 32, 3}, + {12500000, 8000, 1, 7, 8643, 128, 48, 2, 128, 48, 2}, /* 11.025k rate */ - {12000000, 11025, 7, 5264, 128, 32, 2, 128, 32, 2}, - {12000000, 11025, 8, 4672, 128, 24, 3, 128, 24, 3}, - {12500000, 11025, 7, 2253, 128, 32, 2, 128, 32, 2}, + { 705600, 11025, 3, 48, 0, 128, 24, 3, 128, 24, 3}, + {12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2}, + {12000000, 11025, 1, 8, 4672, 128, 24, 3, 128, 24, 3}, + {12500000, 11025, 1, 7, 2253, 128, 32, 2, 128, 32, 2}, /* 16k rate */ - {12000000, 16000, 8, 1920, 128, 24, 2, 128, 24, 2}, - {12000000, 16000, 8, 1920, 128, 16, 3, 128, 16, 3}, - {12500000, 16000, 7, 8643, 128, 24, 2, 128, 24, 2}, + { 512000, 16000, 4, 48, 0, 128, 16, 3, 128, 16, 3}, + { 1024000, 16000, 2, 48, 0, 128, 16, 3, 128, 16, 3}, + {12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2}, + {12000000, 16000, 1, 8, 1920, 128, 16, 3, 128, 16, 3}, + {12500000, 16000, 1, 7, 8643, 128, 24, 2, 128, 24, 2}, /* 22.05k rate */ - {12000000, 22050, 7, 5264, 128, 16, 2, 128, 16, 2}, - {12000000, 22050, 8, 4672, 128, 12, 3, 128, 12, 3}, - {12500000, 22050, 7, 2253, 128, 16, 2, 128, 16, 2}, + { 705600, 22050, 4, 36, 0, 128, 12, 3, 128, 12, 3}, + { 1411200, 22050, 2, 36, 0, 128, 12, 3, 128, 12, 3}, + {12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2}, + {12000000, 22050, 1, 8, 4672, 128, 12, 3, 128, 12, 3}, + {12500000, 22050, 1, 7, 2253, 128, 16, 2, 128, 16, 2}, /* 32k rate */ - {12000000, 32000, 8, 1920, 128, 12, 2, 128, 12, 2}, - {12000000, 32000, 8, 1920, 128, 8, 3, 128, 8, 3}, - {12500000, 32000, 7, 8643, 128, 12, 2, 128, 12, 2}, + { 1024000, 32000, 2, 48, 0, 128, 12, 2, 128, 12, 2}, + { 2048000, 32000, 1, 48, 0, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 1, 8, 1920, 128, 8, 3, 128, 8, 3}, + {12500000, 32000, 1, 7, 8643, 128, 12, 2, 128, 12, 2}, /* 44.1k rate */ - {12000000, 44100, 7, 5264, 128, 8, 2, 128, 8, 2}, - {12000000, 44100, 8, 4672, 128, 6, 3, 128, 6, 3}, - {12500000, 44100, 7, 2253, 128, 8, 2, 128, 8, 2}, + { 1411200, 44100, 2, 32, 0, 128, 8, 2, 128, 8, 2}, + { 2822400, 44100, 1, 32, 0, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 1, 8, 4672, 128, 6, 3, 128, 6, 3}, + {12500000, 44100, 1, 7, 2253, 128, 8, 2, 128, 8, 2}, /* 48k rate */ - {12000000, 48000, 8, 1920, 128, 8, 2, 128, 8, 2}, - {12000000, 48000, 7, 6800, 96, 5, 4, 96, 5, 4}, - {12500000, 48000, 7, 8643, 128, 8, 2, 128, 8, 2}, + { 1536000, 48000, 2, 32, 0, 128, 8, 2, 128, 8, 2}, + { 3072000, 48000, 1, 32, 0, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 1, 7, 6800, 96, 5, 4, 96, 5, 4}, + {12500000, 48000, 1, 7, 8643, 128, 8, 2, 128, 8, 2}, /* 88.2k rate */ - {12000000, 88200, 7, 5264, 64, 8, 2, 64, 8, 2}, - {12000000, 88200, 8, 4672, 64, 6, 3, 64, 6, 3}, - {12500000, 88200, 7, 2253, 64, 8, 2, 64, 8, 2}, + { 2822400, 88200, 2, 16, 0, 64, 8, 2, 64, 8, 2}, + { 5644800, 88200, 1, 16, 0, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 1, 8, 4672, 64, 6, 3, 64, 6, 3}, + {12500000, 88200, 1, 7, 2253, 64, 8, 2, 64, 8, 2}, /* 96k rate */ - {12000000, 96000, 8, 1920, 64, 8, 2, 64, 8, 2}, - {12000000, 96000, 7, 6800, 48, 5, 4, 48, 5, 4}, - {12500000, 96000, 7, 8643, 64, 8, 2, 64, 8, 2}, + { 3072000, 96000, 2, 16, 0, 64, 8, 2, 64, 8, 2}, + { 6144000, 96000, 1, 16, 0, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 1, 7, 6800, 48, 5, 4, 48, 5, 4}, + {12500000, 96000, 1, 7, 8643, 64, 8, 2, 64, 8, 2}, /* 176.4k rate */ - {12000000, 176400, 7, 5264, 32, 8, 2, 32, 8, 2}, - {12000000, 176400, 8, 4672, 32, 6, 3, 32, 6, 3}, - {12500000, 176400, 7, 2253, 32, 8, 2, 32, 8, 2}, + { 5644800, 176400, 2, 8, 0, 32, 8, 2, 32, 8, 2}, + {11289600, 176400, 1, 8, 0, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 1, 8, 4672, 32, 6, 3, 32, 6, 3}, + {12500000, 176400, 1, 7, 2253, 32, 8, 2, 32, 8, 2}, /* 192k rate */ - {12000000, 192000, 8, 1920, 32, 8, 2, 32, 8, 2}, - {12000000, 192000, 7, 6800, 24, 5, 4, 24, 5, 4}, - {12500000, 192000, 7, 8643, 32, 8, 2, 32, 8, 2}, + { 6144000, 192000, 2, 8, 0, 32, 8, 2, 32, 8, 2}, + {12288000, 192000, 1, 8, 0, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 1, 7, 6800, 24, 5, 4, 24, 5, 4}, + {12500000, 192000, 1, 7, 8643, 32, 8, 2, 32, 8, 2}, }; static const char * const ldac_in_text[] = { @@ -888,7 +911,7 @@ static int aic31xx_setup_pll(struct snd_soc_component *component, /* PLL configuration */ snd_soc_component_update_bits(component, AIC31XX_PLLPR, AIC31XX_PLL_MASK, - (aic31xx->p_div << 4) | 0x01); + (aic31xx->p_div << 4) | aic31xx_divs[i].pll_r); snd_soc_component_write(component, AIC31XX_PLLJ, aic31xx_divs[i].pll_j); snd_soc_component_write(component, AIC31XX_PLLDMSB, @@ -941,6 +964,7 @@ static int aic31xx_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); u8 data = 0; dev_dbg(component->dev, "## %s: width %d rate %d\n", @@ -972,6 +996,16 @@ static int aic31xx_hw_params(struct snd_pcm_substream *substream, AIC31XX_IFACE1_DATALEN_MASK, data); + /* + * If BCLK is used as PLL input, the sysclk is determined by the hw + * params. So it must be updated here to match the input frequency. + */ + if (aic31xx->sysclk_id == AIC31XX_PLL_CLKIN_BCLK) { + aic31xx->sysclk = params_rate(params) * params_width(params) * + params_channels(params); + aic31xx->p_div = 1; + } + return aic31xx_setup_pll(component, params); } @@ -1156,6 +1190,7 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, snd_soc_component_update_bits(component, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK, clk_id << AIC31XX_PLL_CLKIN_SHIFT); + aic31xx->sysclk_id = clk_id; aic31xx->sysclk = freq; return 0; @@ -1645,11 +1680,9 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(aic31xx->gpio_reset)) { - if (PTR_ERR(aic31xx->gpio_reset) != -EPROBE_DEFER) - dev_err(aic31xx->dev, "not able to acquire gpio\n"); - return PTR_ERR(aic31xx->gpio_reset); - } + if (IS_ERR(aic31xx->gpio_reset)) + return dev_err_probe(aic31xx->dev, PTR_ERR(aic31xx->gpio_reset), + "not able to acquire gpio\n"); for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) aic31xx->supplies[i].supply = aic31xx_supply_names[i]; @@ -1657,12 +1690,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, ret = devm_regulator_bulk_get(aic31xx->dev, ARRAY_SIZE(aic31xx->supplies), aic31xx->supplies); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(aic31xx->dev, - "Failed to request supplies: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(aic31xx->dev, ret, "Failed to request supplies\n"); aic31xx_configure_ocmv(aic31xx); diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h index 2513922a0292..80d062578fb5 100644 --- a/sound/soc/codecs/tlv320aic31xx.h +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -118,7 +118,7 @@ struct aic31xx_pdata { #define AIC31XX_PLL_CLKIN_MASK GENMASK(3, 2) #define AIC31XX_PLL_CLKIN_SHIFT (2) #define AIC31XX_PLL_CLKIN_MCLK 0x00 -#define AIC31XX_PLL_CLKIN_BCKL 0x01 +#define AIC31XX_PLL_CLKIN_BCLK 0x01 #define AIC31XX_PLL_CLKIN_GPIO1 0x02 #define AIC31XX_PLL_CLKIN_DIN 0x03 #define AIC31XX_CODEC_CLKIN_MASK GENMASK(1, 0) diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 405128ccb4b0..7488a150a138 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -1022,6 +1022,52 @@ static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result) return plug_type; } +static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc) +{ + int hs_threshold, micbias_mv; + + micbias_mv = wcd_mbhc_get_micbias(mbhc); + if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) { + if (mbhc->cfg->micb_mv == micbias_mv) + hs_threshold = mbhc->cfg->hs_thr; + else + hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv; + } else { + hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) / + WCD_MBHC_ADC_MICBIAS_MV); + } + return hs_threshold; +} + +static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc) +{ + bool is_spl_hs = false; + int output_mv, hs_threshold, hph_threshold; + + if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + return false; + + /* Bump up MIC_BIAS2 to 2.7V */ + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true); + usleep_range(10000, 10100); + + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc); + hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc); + + if (!(output_mv > hs_threshold || output_mv < hph_threshold)) + is_spl_hs = true; + + /* Back MIC_BIAS2 to 1.8v if the type is not special headset */ + if (!is_spl_hs) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false); + /* Add 10ms delay for micbias to settle */ + usleep_range(10000, 10100); + } + + return is_spl_hs; +} + static void wcd_correct_swch_plug(struct work_struct *work) { struct wcd_mbhc *mbhc; @@ -1029,12 +1075,14 @@ static void wcd_correct_swch_plug(struct work_struct *work) enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; unsigned long timeout; int pt_gnd_mic_swap_cnt = 0; - int output_mv, cross_conn, hs_threshold, try = 0; + int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv; + bool is_spl_hs = false; bool is_pa_on; mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); component = mbhc->component; + micbias_mv = wcd_mbhc_get_micbias(mbhc); hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); /* Mask ADC COMPLETE interrupt */ @@ -1097,6 +1145,16 @@ correct_plug_type: plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN); + if (output_mv > hs_threshold && !is_spl_hs) { + is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + if (is_spl_hs) { + hs_threshold *= wcd_mbhc_get_micbias(mbhc); + hs_threshold /= micbias_mv; + } + } + if ((output_mv <= hs_threshold) && !is_pa_on) { /* Check for cross connection*/ cross_conn = wcd_check_cross_conn(mbhc); @@ -1122,14 +1180,19 @@ correct_plug_type: } } - if (output_mv > hs_threshold) /* cable is extension cable */ + /* cable is extension cable */ + if (output_mv > hs_threshold || mbhc->force_linein) plug_type = MBHC_PLUG_TYPE_HIGH_HPH; } wcd_mbhc_bcs_enable(mbhc, plug_type, true); - if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) - wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1); + if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { + if (is_spl_hs) + plug_type = MBHC_PLUG_TYPE_HEADSET; + else + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1); + } wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0); wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0); @@ -1176,7 +1239,6 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) struct wcd_mbhc *mbhc = data; unsigned long timeout; int adc_threshold, output_mv, retry = 0; - bool hphpa_on = false; mutex_lock(&mbhc->lock); timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); @@ -1210,10 +1272,6 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) wcd_mbhc_elec_hs_report_unplug(mbhc); wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0); - if (hphpa_on) { - hphpa_on = false; - wcd_mbhc_write_field(mbhc, WCD_MBHC_HPH_PA_EN, 3); - } exit: mutex_unlock(&mbhc->lock); return IRQ_HANDLED; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index bc5d68c53e5a..1e60db4056ad 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -341,7 +341,7 @@ struct wcd9335_codec { int reset_gpio; struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY]; - unsigned int rx_port_value; + unsigned int rx_port_value[WCD9335_RX_MAX]; unsigned int tx_port_value; int hph_l_gain; int hph_r_gain; @@ -1269,10 +1269,11 @@ static const struct snd_kcontrol_new sb_tx8_mux = static int slim_rx_mux_get(struct snd_kcontrol *kc, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); - struct wcd9335_codec *wcd = dev_get_drvdata(dapm->dev); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(w->dapm->dev); + u32 port_id = w->shift; - ucontrol->value.enumerated.item[0] = wcd->rx_port_value; + ucontrol->value.enumerated.item[0] = wcd->rx_port_value[port_id]; return 0; } @@ -1286,9 +1287,9 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc, struct snd_soc_dapm_update *update = NULL; u32 port_id = w->shift; - wcd->rx_port_value = ucontrol->value.enumerated.item[0]; + wcd->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; - switch (wcd->rx_port_value) { + switch (wcd->rx_port_value[port_id]) { case 0: list_del_init(&wcd->rx_chs[port_id].list); break; @@ -1309,11 +1310,11 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc, &wcd->dai[AIF4_PB].slim_ch_list); break; default: - dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value); + dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value[port_id]); goto err; } - snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value, + snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id], e, update); return 0; diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index e63c6b723d76..6c468527fec6 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -3423,7 +3423,7 @@ static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc, { struct soc_enum *e = (struct soc_enum *)kc->private_value; struct snd_soc_component *component; - int reg, val, ret; + int reg, val; component = snd_soc_dapm_kcontrol_component(kc); val = ucontrol->value.enumerated.item[0]; @@ -3446,9 +3446,7 @@ static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc, WCD934X_RX_DLY_ZN_EN_MASK, WCD934X_RX_DLY_ZN_DISABLE); - ret = snd_soc_dapm_put_enum_double(kc, ucontrol); - - return ret; + return snd_soc_dapm_put_enum_double(kc, ucontrol); } static int wcd934x_dec_enum_put(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 67151c7770c6..eff200a07d9f 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -3086,7 +3086,7 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon int micb_num, bool req_en) { struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - int rc, micb_mv; + int micb_mv; if (micb_num != MIC_BIAS_2) return -EINVAL; @@ -3100,9 +3100,7 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd938x->micb2_mv; - rc = wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); - - return rc; + return wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); } static inline void wcd938x_mbhc_get_result_params(struct wcd938x_priv *wcd938x, @@ -4287,7 +4285,7 @@ static int wcd938x_codec_set_sdw_stream(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops wcd938x_sdw_dai_ops = { .hw_params = wcd938x_codec_hw_params, .hw_free = wcd938x_codec_free, - .set_sdw_stream = wcd938x_codec_set_sdw_stream, + .set_stream = wcd938x_codec_set_sdw_stream, }; static struct snd_soc_dai_driver wcd938x_dais[] = { diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 6cb01a8e08fb..f3672e3d1703 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -401,7 +401,7 @@ static int wm_coeff_put(struct snd_kcontrol *kctl, int ret = 0; mutex_lock(&cs_ctl->dsp->pwr_lock); - ret = cs_dsp_coeff_write_ctrl(cs_ctl, p, cs_ctl->len); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len); mutex_unlock(&cs_ctl->dsp->pwr_lock); return ret; @@ -421,7 +421,7 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, if (copy_from_user(cs_ctl->cache, bytes, size)) ret = -EFAULT; else - ret = cs_dsp_coeff_write_ctrl(cs_ctl, cs_ctl->cache, size); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, cs_ctl->cache, size); mutex_unlock(&cs_ctl->dsp->pwr_lock); @@ -464,7 +464,7 @@ static int wm_coeff_get(struct snd_kcontrol *kctl, int ret; mutex_lock(&cs_ctl->dsp->pwr_lock); - ret = cs_dsp_coeff_read_ctrl(cs_ctl, p, cs_ctl->len); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len); mutex_unlock(&cs_ctl->dsp->pwr_lock); return ret; @@ -481,7 +481,7 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, mutex_lock(&cs_ctl->dsp->pwr_lock); - ret = cs_dsp_coeff_read_ctrl(cs_ctl, cs_ctl->cache, size); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) ret = -EFAULT; @@ -537,15 +537,20 @@ static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) return out; } -static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) +static void wm_adsp_ctl_work(struct work_struct *work) { + struct wm_coeff_ctl *ctl = container_of(work, + struct wm_coeff_ctl, + work); struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + struct wm_adsp *dsp = container_of(cs_ctl->dsp, + struct wm_adsp, + cs_dsp); struct snd_kcontrol_new *kcontrol; - int ret; kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); if (!kcontrol) - return -ENOMEM; + return; kcontrol->name = ctl->name; kcontrol->info = wm_coeff_info; @@ -571,29 +576,9 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) break; } - ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); - if (ret < 0) - goto err_kcontrol; - - kfree(kcontrol); - - return 0; + snd_soc_add_component_controls(dsp->component, kcontrol, 1); -err_kcontrol: kfree(kcontrol); - return ret; -} - -static void wm_adsp_ctl_work(struct work_struct *work) -{ - struct wm_coeff_ctl *ctl = container_of(work, - struct wm_coeff_ctl, - work); - struct wm_adsp *dsp = container_of(ctl->cs_ctl->dsp, - struct wm_adsp, - cs_dsp); - - wmfw_add_ctl(dsp, ctl); } static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) @@ -700,7 +685,7 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, if (len > cs_ctl->len) return -EINVAL; - ret = cs_dsp_coeff_write_ctrl(cs_ctl, buf, len); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); if (ret) return ret; @@ -739,7 +724,7 @@ int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, if (len > cs_ctl->len) return -EINVAL; - return cs_dsp_coeff_read_ctrl(cs_ctl, buf, len); + return cs_dsp_coeff_read_ctrl(cs_ctl, 0, buf, len); } EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); @@ -911,11 +896,12 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, struct wm_adsp *dsp = &dsps[mc->shift - 1]; char preload[32]; - snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); + if (dsp->preloaded == ucontrol->value.integer.value[0]) + return 0; - dsp->preloaded = ucontrol->value.integer.value[0]; + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); - if (ucontrol->value.integer.value[0]) + if (ucontrol->value.integer.value[0] || dsp->toggle_preload) snd_soc_component_force_enable_pin(component, preload); else snd_soc_component_disable_pin(component, preload); @@ -924,6 +910,13 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, flush_work(&dsp->boot_work); + dsp->preloaded = ucontrol->value.integer.value[0]; + + if (dsp->toggle_preload) { + snd_soc_component_disable_pin(component, preload); + snd_soc_dapm_sync(dapm); + } + return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); @@ -1448,7 +1441,7 @@ static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) int ret, i; for (i = 0; i < 5; ++i) { - ret = cs_dsp_coeff_read_ctrl(cs_ctl, &coeff_v1, sizeof(coeff_v1)); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1, sizeof(coeff_v1)); if (ret < 0) return ret; diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 0e2f113bd342..7f4fabbc6ad3 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -41,6 +41,14 @@ struct wm_adsp { struct list_head compr_list; struct list_head buffer_list; + + /* + * Flag indicating the preloader widget only needs power toggled + * on state change rather than held on for the duration of the + * preload, useful for devices that can retain firmware memory + * across power down. + */ + bool toggle_preload; }; #define WM_ADSP1(wname, num) \ diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 564b78f3cdd0..0222370ff95d 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1026,7 +1026,7 @@ static const struct snd_soc_dai_ops wsa881x_dai_ops = { .hw_params = wsa881x_hw_params, .hw_free = wsa881x_hw_free, .mute_stream = wsa881x_digital_mute, - .set_sdw_stream = wsa881x_set_sdw_stream, + .set_stream = wsa881x_set_sdw_stream, }; static struct snd_soc_dai_driver wsa881x_dais[] = { diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c index d20ec1571010..6cae0fb08093 100644 --- a/sound/soc/codecs/zl38060.c +++ b/sound/soc/codecs/zl38060.c @@ -589,9 +589,7 @@ static int zl38_spi_probe(struct spi_device *spi) sizeof(template_chip), GFP_KERNEL); if (!priv->gpio_chip) return -ENOMEM; -#ifdef CONFIG_OF_GPIO - priv->gpio_chip->of_node = dev->of_node; -#endif + priv->gpio_chip->parent = dev; err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap); if (err) return err; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 8e05d092790e..10fa38753453 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -311,6 +311,7 @@ config SND_SOC_FSL_ASOC_CARD select SND_SOC_FSL_ESAI select SND_SOC_FSL_SAI select SND_SOC_FSL_SSI + select SND_SOC_TLV320AIC31XX select SND_SOC_WM8994 select MFD_WM8994 help diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 6e6494f9f399..5ee945505281 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -26,6 +26,7 @@ #include "../codecs/wm8962.h" #include "../codecs/wm8960.h" #include "../codecs/wm8994.h" +#include "../codecs/tlv320aic31xx.h" #define CS427x_SYSCLK_MCLK 0 @@ -629,6 +630,16 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { codec_dai_name = "tlv320aic32x4-hifi"; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; + } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { + codec_dai_name = "tlv320dac31xx-hifi"; + priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + priv->dai_link[1].dpcm_capture = 0; + priv->dai_link[2].dpcm_capture = 0; + priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; + priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; + priv->codec_priv.mclk_id = AIC31XX_PLL_CLKIN_BCLK; + priv->card.dapm_routes = audio_map_tx; + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { codec_dai_name = "wm8962"; priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; @@ -842,8 +853,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &priv->card); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); goto asrc_fail; } @@ -888,6 +898,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-cs42888", }, { .compatible = "fsl,imx-audio-cs427x", }, { .compatible = "fsl,imx-audio-tlv320aic32x4", }, + { .compatible = "fsl,imx-audio-tlv320aic31xx", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, { .compatible = "fsl,imx-audio-wm8960", }, diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 24b41881a68f..d7d1536a4f37 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -19,6 +19,7 @@ #include "fsl_asrc.h" #define IDEAL_RATIO_DECIMAL_DEPTH 26 +#define DIVIDER_NUM 64 #define pair_err(fmt, ...) \ dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) @@ -101,6 +102,55 @@ static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = { }, }; +/* + * According to RM, the divider range is 1 ~ 8, + * prescaler is power of 2 from 1 ~ 128. + */ +static int asrc_clk_divider[DIVIDER_NUM] = { + 1, 2, 4, 8, 16, 32, 64, 128, /* divider = 1 */ + 2, 4, 8, 16, 32, 64, 128, 256, /* divider = 2 */ + 3, 6, 12, 24, 48, 96, 192, 384, /* divider = 3 */ + 4, 8, 16, 32, 64, 128, 256, 512, /* divider = 4 */ + 5, 10, 20, 40, 80, 160, 320, 640, /* divider = 5 */ + 6, 12, 24, 48, 96, 192, 384, 768, /* divider = 6 */ + 7, 14, 28, 56, 112, 224, 448, 896, /* divider = 7 */ + 8, 16, 32, 64, 128, 256, 512, 1024, /* divider = 8 */ +}; + +/* + * Check if the divider is available for internal ratio mode + */ +static bool fsl_asrc_divider_avail(int clk_rate, int rate, int *div) +{ + u32 rem, i; + u64 n; + + if (div) + *div = 0; + + if (clk_rate == 0 || rate == 0) + return false; + + n = clk_rate; + rem = do_div(n, rate); + + if (div) + *div = n; + + if (rem != 0) + return false; + + for (i = 0; i < DIVIDER_NUM; i++) { + if (n == asrc_clk_divider[i]) + break; + } + + if (i == DIVIDER_NUM) + return false; + + return true; +} + /** * fsl_asrc_sel_proc - Select the pre-processing and post-processing options * @inrate: input sample rate @@ -330,12 +380,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) enum asrc_word_width input_word_width; enum asrc_word_width output_word_width; u32 inrate, outrate, indiv, outdiv; - u32 clk_index[2], div[2], rem[2]; + u32 clk_index[2], div[2]; u64 clk_rate; int in, out, channels; int pre_proc, post_proc; struct clk *clk; - bool ideal; + bool ideal, div_avail; if (!config) { pair_err("invalid pair config\n"); @@ -415,8 +465,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]]; clk_rate = clk_get_rate(clk); - rem[IN] = do_div(clk_rate, inrate); - div[IN] = (u32)clk_rate; + div_avail = fsl_asrc_divider_avail(clk_rate, inrate, &div[IN]); /* * The divider range is [1, 1024], defined by the hardware. For non- @@ -425,7 +474,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) * only result in different converting speeds. So remainder does not * matter, as long as we keep the divider within its valid range. */ - if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) { + if (div[IN] == 0 || (!ideal && !div_avail)) { pair_err("failed to support input sample rate %dHz by asrck_%x\n", inrate, clk_index[ideal ? OUT : IN]); return -EINVAL; @@ -436,13 +485,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) clk = asrc_priv->asrck_clk[clk_index[OUT]]; clk_rate = clk_get_rate(clk); if (ideal && use_ideal_rate) - rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE); + div_avail = fsl_asrc_divider_avail(clk_rate, IDEAL_RATIO_RATE, &div[OUT]); else - rem[OUT] = do_div(clk_rate, outrate); - div[OUT] = clk_rate; + div_avail = fsl_asrc_divider_avail(clk_rate, outrate, &div[OUT]); /* Output divider has the same limitation as the input one */ - if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) { + if (div[OUT] == 0 || (!ideal && !div_avail)) { pair_err("failed to support output sample rate %dHz by asrck_%x\n", outrate, clk_index[OUT]); return -EINVAL; @@ -621,8 +669,7 @@ static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv, clk_index = asrc_priv->clk_map[j][i]; clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]); /* Only match a perfect clock source with no remainder */ - if (clk_rate != 0 && (clk_rate / rate[j]) <= 1024 && - (clk_rate % rate[j]) == 0) + if (fsl_asrc_divider_avail(clk_rate, rate[j], NULL)) break; } diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index 27b4536dce44..ceaecbe3a25e 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -337,4 +337,4 @@ module_platform_driver(fsl_mqs_driver); MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>"); MODULE_DESCRIPTION("MQS codec driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform: fsl-mqs"); +MODULE_ALIAS("platform:fsl-mqs"); diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index 6f06afd23b16..6f8efd838fcc 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -120,7 +120,7 @@ struct imx_card_data { static struct imx_akcodec_fs_mul ak4458_fs_mul[] = { /* Normal, < 32kHz */ - { .rmin = 8000, .rmax = 24000, .wmin = 1024, .wmax = 1024, }, + { .rmin = 8000, .rmax = 24000, .wmin = 256, .wmax = 1024, }, /* Normal, 32kHz */ { .rmin = 32000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, /* Normal */ @@ -151,8 +151,8 @@ static struct imx_akcodec_fs_mul ak4497_fs_mul[] = { * Table 7 - mapping multiplier and speed mode * Tables 8 & 9 - mapping speed mode and LRCK fs */ - { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, }, /* Normal, <= 32kHz */ - { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, }, /* Normal */ + { .rmin = 8000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, /* Normal, <= 32kHz */ + { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 512, }, /* Normal */ { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, /* Double */ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, /* Quad */ { .rmin = 352800, .rmax = 384000, .wmin = 128, .wmax = 128, }, /* Oct */ @@ -164,7 +164,7 @@ static struct imx_akcodec_fs_mul ak4497_fs_mul[] = { * (Table 4 from datasheet) */ static struct imx_akcodec_fs_mul ak5558_fs_mul[] = { - { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, }, + { .rmin = 8000, .rmax = 32000, .wmin = 512, .wmax = 1024, }, { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, }, { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, @@ -247,13 +247,14 @@ static bool codec_is_akcodec(unsigned int type) } static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params, + int slots, int slot_width) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct imx_card_data *data = snd_soc_card_get_drvdata(rtd->card); const struct imx_card_plat_data *plat_data = data->plat_data; struct dai_link_data *link_data = &data->link_data[rtd->num]; - unsigned int width = link_data->slots * link_data->slot_width; + unsigned int width = slots * slot_width; unsigned int rate = params_rate(params); int i; @@ -349,7 +350,7 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream, /* Set MCLK freq */ if (codec_is_akcodec(plat_data->type)) - mclk_freq = akcodec_get_mclk_rate(substream, params); + mclk_freq = akcodec_get_mclk_rate(substream, params, slots, slot_width); else mclk_freq = params_rate(params) * slots * slot_width; /* Use the maximum freq from DSD512 (512*44100 = 22579200) */ @@ -553,8 +554,23 @@ static int imx_card_parse_of(struct imx_card_data *data) link_data->cpu_sysclk_id = FSL_SAI_CLK_MAST1; /* sai may support mclk/bclk = 1 */ - if (of_find_property(np, "fsl,mclk-equal-bclk", NULL)) + if (of_find_property(np, "fsl,mclk-equal-bclk", NULL)) { link_data->one2one_ratio = true; + } else { + int i; + + /* + * i.MX8MQ don't support one2one ratio, then + * with ak4497 only 16bit case is supported. + */ + for (i = 0; i < ARRAY_SIZE(ak4497_fs_mul); i++) { + if (ak4497_fs_mul[i].rmin == 705600 && + ak4497_fs_mul[i].rmax == 768000) { + ak4497_fs_mul[i].wmin = 32; + ak4497_fs_mul[i].wmax = 32; + } + } + } } link->cpus->of_node = args.np; @@ -563,9 +579,8 @@ static int imx_card_parse_of(struct imx_card_data *data) ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "%s: error getting cpu dai name: %d\n", - link->name, ret); + dev_err_probe(card->dev, ret, + "%s: error getting cpu dai name\n", link->name); goto err; } @@ -573,9 +588,8 @@ static int imx_card_parse_of(struct imx_card_data *data) if (codec) { ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "%s: codec dai not found: %d\n", - link->name, ret); + dev_err_probe(dev, ret, "%s: codec dai not found\n", + link->name); goto err; } @@ -814,11 +828,8 @@ static int imx_card_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return 0; } diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c index f10359a28800..929f69b758af 100644 --- a/sound/soc/fsl/imx-hdmi.c +++ b/sound/soc/fsl/imx-hdmi.c @@ -145,6 +145,8 @@ static int imx_hdmi_probe(struct platform_device *pdev) data->dai.capture_only = false; data->dai.init = imx_hdmi_init; + put_device(&cpu_pdev->dev); + if (of_node_name_eq(cpu_np, "sai")) { data->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; data->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 2f1acd011042..8daced42d55e 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -173,9 +173,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); goto fail; } diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 6c4dadf60355..4446fba755b9 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -70,8 +70,8 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) goto end; ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); end: of_node_put(spdif_np); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 7eb027238327..2b598af8feef 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -593,10 +593,7 @@ int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev) err: asoc_simple_clean_reference(card); - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - - return ret; + return dev_err_probe(dev, ret, "parse error\n"); } EXPORT_SYMBOL_GPL(audio_graph_parse_of); diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index b6049bcfb771..c3947347dda3 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1238,8 +1238,8 @@ int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev, err: devm_kfree(dev, li); - if ((ret < 0) && (ret != -EPROBE_DEFER)) - dev_err(dev, "parse error %d\n", ret); + if (ret < 0) + dev_err_probe(dev, ret, "parse error\n"); return ret; } diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 850e968677f1..a81323d1691d 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -499,57 +499,14 @@ EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets); int asoc_simple_parse_pin_switches(struct snd_soc_card *card, char *prefix) { - const unsigned int nb_controls_max = 16; - const char **strings, *control_name; - struct snd_kcontrol_new *controls; - struct device *dev = card->dev; - unsigned int i, nb_controls; char prop[128]; - int ret; if (!prefix) prefix = ""; snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); - if (!of_property_read_bool(dev->of_node, prop)) - return 0; - - strings = devm_kcalloc(dev, nb_controls_max, - sizeof(*strings), GFP_KERNEL); - if (!strings) - return -ENOMEM; - - ret = of_property_read_string_array(dev->of_node, prop, - strings, nb_controls_max); - if (ret < 0) - return ret; - - nb_controls = (unsigned int)ret; - - controls = devm_kcalloc(dev, nb_controls, - sizeof(*controls), GFP_KERNEL); - if (!controls) - return -ENOMEM; - - for (i = 0; i < nb_controls; i++) { - control_name = devm_kasprintf(dev, GFP_KERNEL, - "%s Switch", strings[i]); - if (!control_name) - return -ENOMEM; - - controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - controls[i].name = control_name; - controls[i].info = snd_soc_dapm_info_pin_switch; - controls[i].get = snd_soc_dapm_get_pin_switch; - controls[i].put = snd_soc_dapm_put_pin_switch; - controls[i].private_value = (unsigned long)strings[i]; - } - - card->controls = controls; - card->num_controls = nb_controls; - - return 0; + return snd_soc_of_parse_pin_switches(card, prop); } EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index a3a7990b5cb6..a89d1cfdda32 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -642,8 +642,7 @@ static int asoc_simple_probe(struct platform_device *pdev) ret = simple_parse_of(priv, li); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); + dev_err_probe(dev, ret, "parse error\n"); goto err; } diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c index 85385a771d80..5da4725d9e16 100644 --- a/sound/soc/generic/test-component.c +++ b/sound/soc/generic/test-component.c @@ -531,8 +531,7 @@ static int test_driver_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct device_node *ep; - const struct of_device_id *of_id = of_match_device(test_of_match, &pdev->dev); - const struct test_adata *adata = of_id->data; + const struct test_adata *adata = of_device_get_match_data(&pdev->dev); struct snd_soc_component_driver *cdriv; struct snd_soc_dai_driver *ddriv; struct test_dai_name *dname; @@ -549,7 +548,7 @@ static int test_driver_probe(struct platform_device *pdev) cdriv = devm_kzalloc(dev, sizeof(*cdriv), GFP_KERNEL); ddriv = devm_kzalloc(dev, sizeof(*ddriv) * num, GFP_KERNEL); dname = devm_kzalloc(dev, sizeof(*dname) * num, GFP_KERNEL); - if (!priv || !cdriv || !ddriv || !dname) + if (!priv || !cdriv || !ddriv || !dname || !adata) return -EINVAL; priv->dev = dev; diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index 1bf5d6edbd32..f1f36f15a503 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -451,11 +451,9 @@ static int img_i2s_in_probe(struct platform_device *pdev) i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); i2s->clk_sys = devm_clk_get(dev, "sys"); - if (IS_ERR(i2s->clk_sys)) { - if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(i2s->clk_sys); - } + if (IS_ERR(i2s->clk_sys)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_sys), + "Failed to acquire clock 'sys'\n"); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index 4f90d36dc7df..28f48ca1508a 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -457,25 +457,19 @@ static int img_i2s_out_probe(struct platform_device *pdev) i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); - if (IS_ERR(i2s->rst)) { - if (PTR_ERR(i2s->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "No top level reset found\n"); - return PTR_ERR(i2s->rst); - } + if (IS_ERR(i2s->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), + "No top level reset found\n"); i2s->clk_sys = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(i2s->clk_sys)) { - if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(i2s->clk_sys); - } + if (IS_ERR(i2s->clk_sys)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_sys), + "Failed to acquire clock 'sys'\n"); i2s->clk_ref = devm_clk_get(&pdev->dev, "ref"); - if (IS_ERR(i2s->clk_ref)) { - if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'ref'\n"); - return PTR_ERR(i2s->clk_ref); - } + if (IS_ERR(i2s->clk_ref)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_ref), + "Failed to acquire clock 'ref'\n"); i2s->suspend_ch_ctl = devm_kcalloc(dev, i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL); diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index ce0f08d3777c..800f247283cd 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -229,25 +229,19 @@ static int img_prl_out_probe(struct platform_device *pdev) prl->base = base; prl->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); - if (IS_ERR(prl->rst)) { - if (PTR_ERR(prl->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "No top level reset found\n"); - return PTR_ERR(prl->rst); - } + if (IS_ERR(prl->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(prl->rst), + "No top level reset found\n"); prl->clk_sys = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(prl->clk_sys)) { - if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(prl->clk_sys); - } + if (IS_ERR(prl->clk_sys)) + return dev_err_probe(dev, PTR_ERR(prl->clk_sys), + "Failed to acquire clock 'sys'\n"); prl->clk_ref = devm_clk_get(&pdev->dev, "ref"); - if (IS_ERR(prl->clk_ref)) { - if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'ref'\n"); - return PTR_ERR(prl->clk_ref); - } + if (IS_ERR(prl->clk_ref)) + return dev_err_probe(dev, PTR_ERR(prl->clk_ref), + "Failed to acquire clock 'ref'\n"); ret = clk_prepare_enable(prl->clk_sys); if (ret) diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 6364eb742f6d..95914d0612fe 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-in.c @@ -739,11 +739,9 @@ static int img_spdif_in_probe(struct platform_device *pdev) spdif->base = base; spdif->clk_sys = devm_clk_get(dev, "sys"); - if (IS_ERR(spdif->clk_sys)) { - if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(spdif->clk_sys); - } + if (IS_ERR(spdif->clk_sys)) + return dev_err_probe(dev, PTR_ERR(spdif->clk_sys), + "Failed to acquire clock 'sys'\n"); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index 858e1b853820..c3189d9ff72f 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -342,25 +342,19 @@ static int img_spdif_out_probe(struct platform_device *pdev) spdif->base = base; spdif->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); - if (IS_ERR(spdif->rst)) { - if (PTR_ERR(spdif->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "No top level reset found\n"); - return PTR_ERR(spdif->rst); - } + if (IS_ERR(spdif->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(spdif->rst), + "No top level reset found\n"); spdif->clk_sys = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(spdif->clk_sys)) { - if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(spdif->clk_sys); - } + if (IS_ERR(spdif->clk_sys)) + return dev_err_probe(dev, PTR_ERR(spdif->clk_sys), + "Failed to acquire clock 'sys'\n"); spdif->clk_ref = devm_clk_get(&pdev->dev, "ref"); - if (IS_ERR(spdif->clk_ref)) { - if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'ref'\n"); - return PTR_ERR(spdif->clk_ref); - } + if (IS_ERR(spdif->clk_ref)) + return dev_err_probe(dev, PTR_ERR(spdif->clk_ref), + "Failed to acquire clock 'ref'\n"); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { diff --git a/sound/soc/img/pistachio-internal-dac.c b/sound/soc/img/pistachio-internal-dac.c index fe181c2e51d6..802c0ee63aa2 100644 --- a/sound/soc/img/pistachio-internal-dac.c +++ b/sound/soc/img/pistachio-internal-dac.c @@ -161,12 +161,9 @@ static int pistachio_internal_dac_probe(struct platform_device *pdev) return PTR_ERR(dac->regmap); dac->supply = devm_regulator_get(dev, "VDD"); - if (IS_ERR(dac->supply)) { - ret = PTR_ERR(dac->supply); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret); - return ret; - } + if (IS_ERR(dac->supply)) + return dev_err_probe(dev, PTR_ERR(dac->supply), + "failed to acquire supply 'VDD-supply'\n"); ret = regulator_enable(dac->supply); if (ret) { diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 5db2f4865bbb..a56dd48c045f 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -653,10 +653,21 @@ static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component, dev_err(rtd->dev, "sst: error code = %d\n", ret_val); return ret_val; } - substream->runtime->delay = str_info->pcm_delay; return str_info->buffer_ptr; } +static snd_pcm_sframes_t sst_soc_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream = substream->runtime->private_data; + struct pcm_stream_info *str_info = &stream->stream_info; + + if (sst_get_stream_status(stream) == SST_PLATFORM_INIT) + return 0; + + return str_info->pcm_delay; +} + static int sst_soc_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { @@ -695,6 +706,7 @@ static const struct snd_soc_component_driver sst_soc_platform_drv = { .open = sst_soc_open, .trigger = sst_soc_trigger, .pointer = sst_soc_pointer, + .delay = sst_soc_delay, .compress_ops = &sst_platform_compress_ops, .pcm_construct = sst_soc_pcm_new, }; diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 2dd5ff7e35ce..34ccefcc30c7 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -467,6 +467,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH (MFD_INTEL_LPSS || COMPILE_TEST)) ||\ (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST)) select SND_SOC_MAX98373_I2C + select SND_SOC_MAX98390 select SND_SOC_RT1011 select SND_SOC_RT1015 select SND_SOC_RT1015P @@ -527,6 +528,25 @@ config SND_SOC_INTEL_SOF_ES8336_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_SOF_NAU8825_MACH + tristate "SOF with nau8825 codec in I2S Mode" + depends on I2C && ACPI && GPIOLIB + depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\ + (MFD_INTEL_LPSS || COMPILE_TEST)) + select SND_SOC_NAU8825 + select SND_SOC_RT1015P + select SND_SOC_MAX98373_I2C + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_SOC_INTEL_HDA_DSP_COMMON + select SND_SOC_INTEL_SOF_MAXIM_COMMON + help + This adds support for ASoC machine driver for SOF platforms + with nau8825 codec. + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL if (SND_SOC_SOF_COMETLAKE && SND_SOC_SOF_HDA_LINK) diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 9ee8ed864f5d..3ea273d27168 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -22,6 +22,7 @@ snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o snd-soc-sof_rt5682-objs := sof_rt5682.o sof_realtek_common.o snd-soc-sof_cs42l42-objs := sof_cs42l42.o snd-soc-sof_es8336-objs := sof_es8336.o +snd-soc-sof_nau8825-objs := sof_nau8825.o sof_realtek_common.o snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o @@ -44,6 +45,7 @@ snd-soc-sof-sdw-objs += sof_sdw.o \ obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_SOF_ES8336_MACH) += snd-soc-sof_es8336.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_NAU8825_MACH) += snd-soc-sof_nau8825.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c index 0a736308052a..ffd497a5b5a5 100644 --- a/sound/soc/intel/boards/bytcht_cx2072x.c +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -147,7 +147,7 @@ static int byt_cht_cx2072x_aif1_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, 48000); } -static struct snd_soc_ops byt_cht_cx2072x_aif1_ops = { +static const struct snd_soc_ops byt_cht_cx2072x_aif1_ops = { .startup = byt_cht_cx2072x_aif1_startup, }; diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c index 67b3c4e97864..115c2bcaabd4 100644 --- a/sound/soc/intel/boards/bytcht_nocodec.c +++ b/sound/soc/intel/boards/bytcht_nocodec.c @@ -93,7 +93,7 @@ static int aif1_startup(struct snd_pcm_substream *substream) &constraints_48000); } -static struct snd_soc_ops aif1_ops = { +static const struct snd_soc_ops aif1_ops = { .startup = aif1_startup, }; diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a0c5f0e9c22a..2ace32c03ec9 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -40,6 +40,8 @@ enum { BYT_RT5640_NO_INTERNAL_MIC_MAP, }; +#define RT5640_JD_SRC_EXT_GPIO 0x0f + enum { BYT_RT5640_JD_SRC_GPIO1 = (RT5640_JD_SRC_GPIO1 << 4), BYT_RT5640_JD_SRC_JD1_IN4P = (RT5640_JD_SRC_JD1_IN4P << 4), @@ -47,6 +49,7 @@ enum { BYT_RT5640_JD_SRC_GPIO2 = (RT5640_JD_SRC_GPIO2 << 4), BYT_RT5640_JD_SRC_GPIO3 = (RT5640_JD_SRC_GPIO3 << 4), BYT_RT5640_JD_SRC_GPIO4 = (RT5640_JD_SRC_GPIO4 << 4), + BYT_RT5640_JD_SRC_EXT_GPIO = (RT5640_JD_SRC_EXT_GPIO << 4) }; enum { @@ -79,6 +82,7 @@ enum { #define BYT_RT5640_LINEOUT_AS_HP2 BIT(26) #define BYT_RT5640_HSMIC2_ON_IN1 BIT(27) #define BYT_RT5640_JD_HP_ELITEP_1000G2 BIT(28) +#define BYT_RT5640_USE_AMCR0F28 BIT(29) #define BYTCR_INPUT_DEFAULTS \ (BYT_RT5640_IN3_MAP | \ @@ -93,6 +97,7 @@ enum { struct byt_rt5640_private { struct snd_soc_jack jack; struct snd_soc_jack jack2; + struct rt5640_set_jack_data jack_data; struct gpio_desc *hsmic_detect; struct clk *mclk; struct device *codec_dev; @@ -597,7 +602,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_OVCD_TH_2000UA | BYT_RT5640_OVCD_SF_0P75 | BYT_RT5640_SSP0_AIF1 | - BYT_RT5640_MCLK_EN), + BYT_RT5640_MCLK_EN | + BYT_RT5640_USE_AMCR0F28), }, { .matches = { @@ -624,6 +630,19 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_EXT_GPIO | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN | + BYT_RT5640_USE_AMCR0F28), + }, { /* Chuwi Vi8 (CWI506) */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), @@ -1080,9 +1099,11 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, } if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) { - props[cnt++] = PROPERTY_ENTRY_U32( - "realtek,jack-detect-source", - BYT_RT5640_JDSRC(byt_rt5640_quirk)); + if (BYT_RT5640_JDSRC(byt_rt5640_quirk) != RT5640_JD_SRC_EXT_GPIO) { + props[cnt++] = PROPERTY_ENTRY_U32( + "realtek,jack-detect-source", + BYT_RT5640_JDSRC(byt_rt5640_quirk)); + } props[cnt++] = PROPERTY_ENTRY_U32( "realtek,over-current-threshold-microamp", @@ -1109,6 +1130,51 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, return ret; } +/* Some Android devs specify IRQs/GPIOS in a special AMCR0F28 ACPI device */ +static const struct acpi_gpio_params amcr0f28_jd_gpio = { 1, 0, false }; + +static const struct acpi_gpio_mapping amcr0f28_gpios[] = { + { "rt5640-jd-gpios", &amcr0f28_jd_gpio, 1 }, + { } +}; + +static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card) +{ + struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); + struct rt5640_set_jack_data *data = &priv->jack_data; + struct acpi_device *adev; + int ret = 0; + + adev = acpi_dev_get_first_match_dev("AMCR0F28", "1", -1); + if (!adev) { + dev_err(card->dev, "error cannot find AMCR0F28 adev\n"); + return -ENOENT; + } + + data->codec_irq_override = acpi_dev_gpio_irq_get(adev, 0); + if (data->codec_irq_override < 0) { + ret = data->codec_irq_override; + dev_err(card->dev, "error %d getting codec IRQ\n", ret); + goto put_adev; + } + + if (BYT_RT5640_JDSRC(byt_rt5640_quirk) == RT5640_JD_SRC_EXT_GPIO) { + acpi_dev_add_driver_gpios(adev, amcr0f28_gpios); + data->jd_gpio = devm_fwnode_gpiod_get(card->dev, acpi_fwnode_handle(adev), + "rt5640-jd", GPIOD_IN, "rt5640-jd"); + acpi_dev_remove_driver_gpios(adev); + + if (IS_ERR(data->jd_gpio)) { + ret = PTR_ERR(data->jd_gpio); + dev_err(card->dev, "error %d getting jd GPIO\n", ret); + } + } + +put_adev: + acpi_dev_put(adev); + return ret; +} + static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; @@ -1244,7 +1310,14 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) } snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_soc_component_set_jack(component, &priv->jack, NULL); + + if (byt_rt5640_quirk & BYT_RT5640_USE_AMCR0F28) { + ret = byt_rt5640_get_amcr0f28_settings(card); + if (ret) + return ret; + } + + snd_soc_component_set_jack(component, &priv->jack, &priv->jack_data); } if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) { @@ -1448,7 +1521,8 @@ static int byt_rt5640_resume(struct snd_soc_card *card) for_each_card_components(card, component) { if (!strcmp(component->name, byt_rt5640_codec_name)) { dev_dbg(component->dev, "re-enabling jack detect after resume\n"); - snd_soc_component_set_jack(component, &priv->jack, NULL); + snd_soc_component_set_jack(component, &priv->jack, + &priv->jack_data); break; } } diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c index efdc4bc4bb1f..5c31ddc0884a 100644 --- a/sound/soc/intel/boards/hda_dsp_common.c +++ b/sound/soc/intel/boards/hda_dsp_common.c @@ -68,7 +68,7 @@ int hda_dsp_hdmi_build_controls(struct snd_soc_card *card, hpcm->pcm = NULL; hpcm->device = SNDRV_PCM_INVALID_DEVICE; dev_warn(card->dev, - "%s: no PCM in topology for HDMI converter %d\n\n", + "%s: no PCM in topology for HDMI converter %d\n", __func__, i); } i++; diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index e66dfe666915..112e89951da0 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -5,6 +5,7 @@ #include <linux/string.h> #include <sound/pcm.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include <sound/soc-dai.h> #include <sound/soc-dapm.h> #include <uapi/sound/asound.h> @@ -134,6 +135,185 @@ void max_98373_set_codec_conf(struct snd_soc_card *card) EXPORT_SYMBOL_NS(max_98373_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); /* + * Maxim MAX98390 + */ +static const struct snd_soc_dapm_route max_98390_dapm_routes[] = { + /* speaker */ + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, +}; + +static const struct snd_kcontrol_new max_98390_tt_kcontrols[] = { + SOC_DAPM_PIN_SWITCH("TL Spk"), + SOC_DAPM_PIN_SWITCH("TR Spk"), +}; + +static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = { + SND_SOC_DAPM_SPK("TL Spk", NULL), + SND_SOC_DAPM_SPK("TR Spk", NULL), +}; + +static const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = { + /* Tweeter speaker */ + { "TL Spk", NULL, "Tweeter Left BE_OUT" }, + { "TR Spk", NULL, "Tweeter Right BE_OUT" }, +}; + +static struct snd_soc_codec_conf max_98390_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), + .name_prefix = "Left", + }, +}; + +static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME), + .name_prefix = "Tweeter Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV3_NAME), + .name_prefix = "Tweeter Left", + }, +}; + +struct snd_soc_dai_link_component max_98390_components[] = { + { + .name = MAX_98390_DEV0_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV1_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, +}; +EXPORT_SYMBOL_NS(max_98390_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +struct snd_soc_dai_link_component max_98390_4spk_components[] = { + { + .name = MAX_98390_DEV0_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV1_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV2_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV3_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, +}; +EXPORT_SYMBOL_NS(max_98390_4spk_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +static int max_98390_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int i; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (i >= ARRAY_SIZE(max_98390_4spk_components)) { + dev_err(codec_dai->dev, "invalid codec index %d\n", i); + return -ENODEV; + } + + if (!strcmp(codec_dai->component->name, MAX_98390_DEV0_NAME)) { + /* DEV0 tdm slot configuration Right */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x01, 3, 4, 32); + } + if (!strcmp(codec_dai->component->name, MAX_98390_DEV1_NAME)) { + /* DEV1 tdm slot configuration Left */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x02, 3, 4, 32); + } + + if (!strcmp(codec_dai->component->name, MAX_98390_DEV2_NAME)) { + /* DEVi2 tdm slot configuration Tweeter Right */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x04, 3, 4, 32); + } + if (!strcmp(codec_dai->component->name, MAX_98390_DEV3_NAME)) { + /* DEV3 tdm slot configuration Tweeter Left */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x08, 3, 4, 32); + } + } + return 0; +} + +int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + /* add regular speakers dapm route */ + ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes, + ARRAY_SIZE(max_98390_dapm_routes)); + if (ret) { + dev_err(rtd->dev, "unable to add Left/Right Speaker dapm, ret %d\n", ret); + return ret; + } + + /* add widgets/controls/dapm for tweeter speakers */ + if (acpi_dev_present("MX98390", "3", -1)) { + ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets, + ARRAY_SIZE(max_98390_tt_dapm_widgets)); + + if (ret) { + dev_err(rtd->dev, "unable to add tweeter dapm controls, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols, + ARRAY_SIZE(max_98390_tt_kcontrols)); + if (ret) { + dev_err(rtd->dev, "unable to add tweeter card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes, + ARRAY_SIZE(max_98390_tt_dapm_routes)); + if (ret) + dev_err(rtd->dev, + "unable to add Tweeter Left/Right Speaker dapm, ret %d\n", ret); + } + return ret; +} +EXPORT_SYMBOL_NS(max_98390_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +const struct snd_soc_ops max_98390_ops = { + .hw_params = max_98390_hw_params, +}; +EXPORT_SYMBOL_NS(max_98390_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +void max_98390_set_codec_conf(struct snd_soc_card *card, int ch) +{ + if (ch == ARRAY_SIZE(max_98390_4spk_codec_conf)) { + card->codec_conf = max_98390_4spk_codec_conf; + card->num_configs = ARRAY_SIZE(max_98390_4spk_codec_conf); + } else { + card->codec_conf = max_98390_codec_conf; + card->num_configs = ARRAY_SIZE(max_98390_codec_conf); + } +} +EXPORT_SYMBOL_NS(max_98390_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +/* * Maxim MAX98357A/MAX98360A */ static const struct snd_kcontrol_new max_98357a_kcontrols[] = { diff --git a/sound/soc/intel/boards/sof_maxim_common.h b/sound/soc/intel/boards/sof_maxim_common.h index 3ff5e8fec4de..7a8c53049e4d 100644 --- a/sound/soc/intel/boards/sof_maxim_common.h +++ b/sound/soc/intel/boards/sof_maxim_common.h @@ -25,6 +25,22 @@ void max_98373_set_codec_conf(struct snd_soc_card *card); int max_98373_trigger(struct snd_pcm_substream *substream, int cmd); /* + * Maxim MAX98390 + */ +#define MAX_98390_CODEC_DAI "max98390-aif1" +#define MAX_98390_DEV0_NAME "i2c-MX98390:00" +#define MAX_98390_DEV1_NAME "i2c-MX98390:01" +#define MAX_98390_DEV2_NAME "i2c-MX98390:02" +#define MAX_98390_DEV3_NAME "i2c-MX98390:03" + +extern struct snd_soc_dai_link_component max_98390_components[2]; +extern struct snd_soc_dai_link_component max_98390_4spk_components[4]; +extern const struct snd_soc_ops max_98390_ops; + +void max_98390_set_codec_conf(struct snd_soc_card *card, int ch); +int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd); + +/* * Maxim MAX98357A/MAX98360A */ #define MAX_98357A_CODEC_DAI "HiFi" diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c new file mode 100644 index 000000000000..33de043b66c6 --- /dev/null +++ b/sound/soc/intel/boards/sof_nau8825.c @@ -0,0 +1,651 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright(c) 2021 Intel Corporation. +// Copyright(c) 2021 Nuvoton Corporation. + +/* + * Intel SOF Machine Driver with Nuvoton headphone codec NAU8825 + * and speaker codec RT1019P MAX98360a or MAX98373 + */ +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/dmi.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/sof.h> +#include <sound/soc-acpi.h> +#include "../../codecs/nau8825.h" +#include "../common/soc-intel-quirks.h" +#include "hda_dsp_common.h" +#include "sof_realtek_common.h" +#include "sof_maxim_common.h" + +#define NAME_SIZE 32 + +#define SOF_NAU8825_SSP_CODEC(quirk) ((quirk) & GENMASK(2, 0)) +#define SOF_NAU8825_SSP_CODEC_MASK (GENMASK(2, 0)) +#define SOF_SPEAKER_AMP_PRESENT BIT(3) +#define SOF_NAU8825_SSP_AMP_SHIFT 4 +#define SOF_NAU8825_SSP_AMP_MASK (GENMASK(6, 4)) +#define SOF_NAU8825_SSP_AMP(quirk) \ + (((quirk) << SOF_NAU8825_SSP_AMP_SHIFT) & SOF_NAU8825_SSP_AMP_MASK) +#define SOF_NAU8825_NUM_HDMIDEV_SHIFT 7 +#define SOF_NAU8825_NUM_HDMIDEV_MASK (GENMASK(9, 7)) +#define SOF_NAU8825_NUM_HDMIDEV(quirk) \ + (((quirk) << SOF_NAU8825_NUM_HDMIDEV_SHIFT) & SOF_NAU8825_NUM_HDMIDEV_MASK) + +/* BT audio offload: reserve 3 bits for future */ +#define SOF_BT_OFFLOAD_SSP_SHIFT 10 +#define SOF_BT_OFFLOAD_SSP_MASK (GENMASK(12, 10)) +#define SOF_BT_OFFLOAD_SSP(quirk) \ + (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) +#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(13) +#define SOF_RT1019P_SPEAKER_AMP_PRESENT BIT(14) +#define SOF_MAX98373_SPEAKER_AMP_PRESENT BIT(15) +#define SOF_MAX98360A_SPEAKER_AMP_PRESENT BIT(16) + +static unsigned long sof_nau8825_quirk = SOF_NAU8825_SSP_CODEC(0); + +struct sof_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct sof_card_private { + struct clk *mclk; + struct snd_soc_jack sof_headset; + struct list_head hdmi_pcm_list; +}; + +static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); + struct sof_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int sof_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + + struct snd_soc_jack *jack; + int ret; + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sof_headset, NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + jack = &ctx->sof_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) { + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + return ret; + } + + return ret; +}; + +static void sof_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + + snd_soc_component_set_jack(component, NULL, NULL); +} + +static int sof_nau8825_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + int clk_freq, ret; + + clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */ + + if (clk_freq <= 0) { + dev_err(rtd->dev, "get bclk freq failed: %d\n", clk_freq); + return -EINVAL; + } + + /* Configure clock for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret); + return ret; + } + + /* Configure pll for codec */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq, + params_rate(params) * 256); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret); + return ret; + } + + return ret; +} + +static struct snd_soc_ops sof_nau8825_ops = { + .hw_params = sof_nau8825_hw_params, +}; + +static struct snd_soc_dai_link_component platform_component[] = { + { + /* name might be overridden during probe */ + .name = "0000:00:1f.3" + } +}; + +static int sof_card_late_probe(struct snd_soc_card *card) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm = &card->dapm; + struct sof_hdmi_pcm *pcm; + int err; + + if (list_empty(&ctx->hdmi_pcm_list)) + return -EINVAL; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head); + + if (sof_nau8825_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT) { + /* Disable Left and Right Spk pin after boot */ + snd_soc_dapm_disable_pin(dapm, "Left Spk"); + snd_soc_dapm_disable_pin(dapm, "Right Spk"); + err = snd_soc_dapm_sync(dapm); + if (err < 0) + return err; + } + + return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component); +} + +static const struct snd_kcontrol_new sof_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_kcontrol_new speaker_controls[] = { + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget sof_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), +}; + +static const struct snd_soc_dapm_widget speaker_widgets[] = { + SND_SOC_DAPM_SPK("Spk", NULL), +}; + +static const struct snd_soc_dapm_widget dmic_widgets[] = { + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route sof_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* other jacks */ + { "MIC", NULL, "Headset Mic" }, +}; + +static const struct snd_soc_dapm_route speaker_map[] = { + /* speaker */ + { "Spk", NULL, "Speaker" }, +}; + +static const struct snd_soc_dapm_route dmic_map[] = { + /* digital mics */ + {"DMic", NULL, "SoC DMIC"}, +}; + +static int speaker_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, speaker_widgets, + ARRAY_SIZE(speaker_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, speaker_controls, + ARRAY_SIZE(speaker_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map, + ARRAY_SIZE(speaker_map)); + + if (ret) + dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret); + return ret; +} + +static int dmic_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets, + ARRAY_SIZE(dmic_widgets)); + if (ret) { + dev_err(card->dev, "DMic widget addition failed: %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, dmic_map, + ARRAY_SIZE(dmic_map)); + + if (ret) + dev_err(card->dev, "DMic map addition failed: %d\n", ret); + + return ret; +} + +/* sof audio machine driver for nau8825 codec */ +static struct snd_soc_card sof_audio_card_nau8825 = { + .name = "nau8825", /* the sof- prefix is added by the core */ + .owner = THIS_MODULE, + .controls = sof_controls, + .num_controls = ARRAY_SIZE(sof_controls), + .dapm_widgets = sof_widgets, + .num_dapm_widgets = ARRAY_SIZE(sof_widgets), + .dapm_routes = sof_map, + .num_dapm_routes = ARRAY_SIZE(sof_map), + .fully_routed = true, + .late_probe = sof_card_late_probe, +}; + +static struct snd_soc_dai_link_component nau8825_component[] = { + { + .name = "i2c-10508825:00", + .dai_name = "nau8825-hifi", + } +}; + +static struct snd_soc_dai_link_component dmic_component[] = { + { + .name = "dmic-codec", + .dai_name = "dmic-hifi", + } +}; + +static struct snd_soc_dai_link_component rt1019p_component[] = { + { + .name = "RTL1019:00", + .dai_name = "HiFi", + } +}; + +static struct snd_soc_dai_link_component dummy_component[] = { + { + .name = "snd-soc-dummy", + .dai_name = "snd-soc-dummy-dai", + } +}; + +static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, + int ssp_codec, + int ssp_amp, + int dmic_be_num, + int hdmi_num) +{ + struct snd_soc_dai_link_component *idisp_components; + struct snd_soc_dai_link_component *cpus; + struct snd_soc_dai_link *links; + int i, id = 0; + + links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * + sof_audio_card_nau8825.num_links, GFP_KERNEL); + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) * + sof_audio_card_nau8825.num_links, GFP_KERNEL); + if (!links || !cpus) + goto devm_err; + + /* codec SSP */ + links[id].name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d-Codec", ssp_codec); + if (!links[id].name) + goto devm_err; + + links[id].id = id; + links[id].codecs = nau8825_component; + links[id].num_codecs = ARRAY_SIZE(nau8825_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].init = sof_nau8825_codec_init; + links[id].exit = sof_nau8825_codec_exit; + links[id].ops = &sof_nau8825_ops; + links[id].dpcm_playback = 1; + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + links[id].cpus = &cpus[id]; + links[id].num_cpus = 1; + + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d Pin", + ssp_codec); + if (!links[id].cpus->dai_name) + goto devm_err; + + id++; + + /* dmic */ + if (dmic_be_num > 0) { + /* at least we have dmic01 */ + links[id].name = "dmic01"; + links[id].cpus = &cpus[id]; + links[id].cpus->dai_name = "DMIC01 Pin"; + links[id].init = dmic_init; + if (dmic_be_num > 1) { + /* set up 2 BE links at most */ + links[id + 1].name = "dmic16k"; + links[id + 1].cpus = &cpus[id + 1]; + links[id + 1].cpus->dai_name = "DMIC16k Pin"; + dmic_be_num = 2; + } + } + + for (i = 0; i < dmic_be_num; i++) { + links[id].id = id; + links[id].num_cpus = 1; + links[id].codecs = dmic_component; + links[id].num_codecs = ARRAY_SIZE(dmic_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].ignore_suspend = 1; + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + id++; + } + + /* HDMI */ + if (hdmi_num > 0) { + idisp_components = devm_kzalloc(dev, + sizeof(struct snd_soc_dai_link_component) * + hdmi_num, GFP_KERNEL); + if (!idisp_components) + goto devm_err; + } + for (i = 1; i <= hdmi_num; i++) { + links[id].name = devm_kasprintf(dev, GFP_KERNEL, + "iDisp%d", i); + if (!links[id].name) + goto devm_err; + + links[id].id = id; + links[id].cpus = &cpus[id]; + links[id].num_cpus = 1; + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "iDisp%d Pin", i); + if (!links[id].cpus->dai_name) + goto devm_err; + + idisp_components[i - 1].name = "ehdaudio0D2"; + idisp_components[i - 1].dai_name = devm_kasprintf(dev, + GFP_KERNEL, + "intel-hdmi-hifi%d", + i); + if (!idisp_components[i - 1].dai_name) + goto devm_err; + + links[id].codecs = &idisp_components[i - 1]; + links[id].num_codecs = 1; + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].init = sof_hdmi_init; + links[id].dpcm_playback = 1; + links[id].no_pcm = 1; + id++; + } + + /* speaker amp */ + if (sof_nau8825_quirk & SOF_SPEAKER_AMP_PRESENT) { + links[id].name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d-Codec", ssp_amp); + if (!links[id].name) + goto devm_err; + + links[id].id = id; + if (sof_nau8825_quirk & SOF_RT1019P_SPEAKER_AMP_PRESENT) { + links[id].codecs = rt1019p_component; + links[id].num_codecs = ARRAY_SIZE(rt1019p_component); + links[id].init = speaker_codec_init; + } else if (sof_nau8825_quirk & + SOF_MAX98373_SPEAKER_AMP_PRESENT) { + links[id].codecs = max_98373_components; + links[id].num_codecs = ARRAY_SIZE(max_98373_components); + links[id].init = max_98373_spk_codec_init; + links[id].ops = &max_98373_ops; + /* feedback stream */ + links[id].dpcm_capture = 1; + } else if (sof_nau8825_quirk & + SOF_MAX98360A_SPEAKER_AMP_PRESENT) { + max_98360a_dai_link(&links[id]); + } else { + goto devm_err; + } + + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].dpcm_playback = 1; + links[id].no_pcm = 1; + links[id].cpus = &cpus[id]; + links[id].num_cpus = 1; + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d Pin", + ssp_amp); + if (!links[id].cpus->dai_name) + goto devm_err; + id++; + } + + /* BT audio offload */ + if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { + int port = (sof_nau8825_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> + SOF_BT_OFFLOAD_SSP_SHIFT; + + links[id].id = id; + links[id].cpus = &cpus[id]; + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d Pin", port); + if (!links[id].cpus->dai_name) + goto devm_err; + links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); + if (!links[id].name) + goto devm_err; + links[id].codecs = dummy_component; + links[id].num_codecs = ARRAY_SIZE(dummy_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].dpcm_playback = 1; + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + links[id].num_cpus = 1; + } + + return links; +devm_err: + return NULL; +} + +static int sof_audio_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_link *dai_links; + struct snd_soc_acpi_mach *mach; + struct sof_card_private *ctx; + int dmic_be_num, hdmi_num; + int ret, ssp_amp, ssp_codec; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (pdev->id_entry && pdev->id_entry->driver_data) + sof_nau8825_quirk = (unsigned long)pdev->id_entry->driver_data; + + mach = pdev->dev.platform_data; + + /* A speaker amp might not be present when the quirk claims one is. + * Detect this via whether the machine driver match includes quirk_data. + */ + if ((sof_nau8825_quirk & SOF_SPEAKER_AMP_PRESENT) && !mach->quirk_data) + sof_nau8825_quirk &= ~SOF_SPEAKER_AMP_PRESENT; + + dev_dbg(&pdev->dev, "sof_nau8825_quirk = %lx\n", sof_nau8825_quirk); + + /* default number of DMIC DAI's */ + dmic_be_num = 2; + hdmi_num = (sof_nau8825_quirk & SOF_NAU8825_NUM_HDMIDEV_MASK) >> + SOF_NAU8825_NUM_HDMIDEV_SHIFT; + /* default number of HDMI DAI's */ + if (!hdmi_num) + hdmi_num = 3; + + ssp_amp = (sof_nau8825_quirk & SOF_NAU8825_SSP_AMP_MASK) >> + SOF_NAU8825_SSP_AMP_SHIFT; + + ssp_codec = sof_nau8825_quirk & SOF_NAU8825_SSP_CODEC_MASK; + + /* compute number of dai links */ + sof_audio_card_nau8825.num_links = 1 + dmic_be_num + hdmi_num; + + if (sof_nau8825_quirk & SOF_SPEAKER_AMP_PRESENT) + sof_audio_card_nau8825.num_links++; + + if (sof_nau8825_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT) + max_98373_set_codec_conf(&sof_audio_card_nau8825); + + if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) + sof_audio_card_nau8825.num_links++; + + dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp, + dmic_be_num, hdmi_num); + if (!dai_links) + return -ENOMEM; + + sof_audio_card_nau8825.dai_link = dai_links; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + sof_audio_card_nau8825.dev = &pdev->dev; + + /* set platform name for each dailink */ + ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_nau8825, + mach->mach_params.platform); + if (ret) + return ret; + + snd_soc_card_set_drvdata(&sof_audio_card_nau8825, ctx); + + return devm_snd_soc_register_card(&pdev->dev, + &sof_audio_card_nau8825); +} + +static const struct platform_device_id board_ids[] = { + { + .name = "sof_nau8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_NAU8825_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + + }, + { + .name = "adl_rt1019p_nau8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT1019P_SPEAKER_AMP_PRESENT | + SOF_NAU8825_SSP_AMP(2) | + SOF_NAU8825_NUM_HDMIDEV(4)), + }, + { + .name = "adl_max98373_nau8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98373_SPEAKER_AMP_PRESENT | + SOF_NAU8825_SSP_AMP(1) | + SOF_NAU8825_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, + { + /* The limitation of length of char array, shorten the name */ + .name = "adl_mx98360a_nau8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98360A_SPEAKER_AMP_PRESENT | + SOF_NAU8825_SSP_AMP(1) | + SOF_NAU8825_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + + }, + { } +}; +MODULE_DEVICE_TABLE(platform, board_ids); + +static struct platform_driver sof_audio = { + .probe = sof_audio_probe, + .driver = { + .name = "sof_nau8825", + .pm = &snd_soc_pm_ops, + }, + .id_table = board_ids, +}; +module_platform_driver(sof_audio) + +/* Module information */ +MODULE_DESCRIPTION("SOF Audio Machine driver for NAU8825"); +MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>"); +MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index 2ec34f8df9e1..4cf131310ad3 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -12,12 +12,13 @@ #include <sound/soc-dapm.h> #include <uapi/sound/asound.h> #include "../../codecs/rt1011.h" +#include "../../codecs/rt1015.h" #include "sof_realtek_common.h" /* * Current only 2-amp configuration is supported for rt1011 */ -static const struct snd_soc_dapm_route rt1011_dapm_routes[] = { +static const struct snd_soc_dapm_route speaker_map_lr[] = { /* speaker */ { "Left Spk", NULL, "Left SPO" }, { "Right Spk", NULL, "Right SPO" }, @@ -117,8 +118,8 @@ static int rt1011_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card = rtd->card; int ret; - ret = snd_soc_dapm_add_routes(&card->dapm, rt1011_dapm_routes, - ARRAY_SIZE(rt1011_dapm_routes)); + ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map_lr, + ARRAY_SIZE(speaker_map_lr)); if (ret) dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret); return ret; @@ -241,3 +242,115 @@ void sof_rt1015p_codec_conf(struct snd_soc_card *card) card->codec_conf = rt1015p_codec_confs; card->num_configs = ARRAY_SIZE(rt1015p_codec_confs); } + +/* + * RT1015 audio amplifier + */ + +static int rt1015_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int i, fs = 64, ret; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK, + params_rate(params) * fs, + params_rate(params) * 256); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL, + params_rate(params) * 256, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + + return 0; +} + +static int rt1015_hw_params_pll_and_tdm(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int i, fs = 100, ret; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK, + params_rate(params) * fs, + params_rate(params) * 256); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL, + params_rate(params) * 256, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + /* rx slot 1 for RT1015_DEV0_NAME */ + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 0), + 0x0, 0x1, 4, 24); + if (ret) + return ret; + + /* rx slot 2 for RT1015_DEV1_NAME */ + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 1), + 0x0, 0x2, 4, 24); + if (ret) + return ret; + + return 0; +} + +static struct snd_soc_ops rt1015_ops = { + .hw_params = rt1015_hw_params, +}; + +static struct snd_soc_codec_conf rt1015_amp_conf[] = { + { + .dlc = COMP_CODEC_CONF(RT1015_DEV0_NAME), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF(RT1015_DEV1_NAME), + .name_prefix = "Right", + }, +}; + +static struct snd_soc_dai_link_component rt1015_components[] = { + { + .name = RT1015_DEV0_NAME, + .dai_name = RT1015_CODEC_DAI, + }, + { + .name = RT1015_DEV1_NAME, + .dai_name = RT1015_CODEC_DAI, + }, +}; + +static int speaker_codec_init_lr(struct snd_soc_pcm_runtime *rtd) +{ + return snd_soc_dapm_add_routes(&rtd->card->dapm, speaker_map_lr, + ARRAY_SIZE(speaker_map_lr)); +} + +void sof_rt1015_codec_conf(struct snd_soc_card *card) +{ + card->codec_conf = rt1015_amp_conf; + card->num_configs = ARRAY_SIZE(rt1015_amp_conf); +} + +void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs) +{ + link->codecs = rt1015_components; + link->num_codecs = ARRAY_SIZE(rt1015_components); + link->init = speaker_codec_init_lr; + link->ops = &rt1015_ops; + + if (fs == 100) + rt1015_ops.hw_params = rt1015_hw_params_pll_and_tdm; +} diff --git a/sound/soc/intel/boards/sof_realtek_common.h b/sound/soc/intel/boards/sof_realtek_common.h index cb0b49b2855c..228ac9c08430 100644 --- a/sound/soc/intel/boards/sof_realtek_common.h +++ b/sound/soc/intel/boards/sof_realtek_common.h @@ -28,4 +28,11 @@ void sof_rt1011_codec_conf(struct snd_soc_card *card); void sof_rt1015p_dai_link(struct snd_soc_dai_link *link); void sof_rt1015p_codec_conf(struct snd_soc_card *card); +#define RT1015_CODEC_DAI "rt1015-aif" +#define RT1015_DEV0_NAME "i2c-10EC1015:00" +#define RT1015_DEV1_NAME "i2c-10EC1015:01" + +void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs); +void sof_rt1015_codec_conf(struct snd_soc_card *card); + #endif /* __SOF_REALTEK_COMMON_H */ diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index c41f386b4138..bd6d2e7dea53 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -20,7 +20,6 @@ #include <sound/rt5682.h> #include <sound/rt5682s.h> #include <sound/soc-acpi.h> -#include "../../codecs/rt1015.h" #include "../../codecs/rt5682.h" #include "../../codecs/rt5682s.h" #include "../../codecs/hdac_hdmi.h" @@ -59,6 +58,9 @@ (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(22) #define SOF_RT5682S_HEADPHONE_CODEC_PRESENT BIT(23) +#define SOF_MAX98390_SPEAKER_AMP_PRESENT BIT(24) +#define SOF_MAX98390_TWEETER_SPEAKER_PRESENT BIT(25) + /* Default: MCLK on, MCLK 19.2M, SSP0 */ static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN | @@ -179,6 +181,36 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { SOF_RT5682_SSP_AMP(2) | SOF_RT5682_NUM_HDMIDEV(4)), }, + { + .callback = sof_rt5682_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"), + }, + .driver_data = (void *)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98390_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(2) | + SOF_RT5682_NUM_HDMIDEV(4)), + }, + { + .callback = sof_rt5682_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S_4SPK"), + }, + .driver_data = (void *)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98390_SPEAKER_AMP_PRESENT | + SOF_MAX98390_TWEETER_SPEAKER_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + + }, {} }; @@ -367,67 +399,6 @@ static struct snd_soc_ops sof_rt5682_ops = { .hw_params = sof_rt5682_hw_params, }; -static int sof_rt1015_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai; - int i, fs, ret; - - if (!snd_soc_card_get_codec_dai(card, "rt1015-aif")) - return 0; - - if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_100FS) - fs = 100; - else - fs = 64; - - for_each_rtd_codec_dais(rtd, i, codec_dai) { - ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK, - params_rate(params) * fs, - params_rate(params) * 256); - if (ret < 0) { - dev_err(card->dev, "failed to set pll\n"); - return ret; - } - /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL, - params_rate(params) * 256, - SND_SOC_CLOCK_IN); - if (ret < 0) { - dev_err(card->dev, "failed to set sysclk\n"); - return ret; - } - - if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_100FS) { - if (!strcmp(codec_dai->component->name, "i2c-10EC1015:00")) { - ret = snd_soc_dai_set_tdm_slot(codec_dai, - 0x0, 0x1, 4, 24); - if (ret < 0) { - dev_err(card->dev, "failed to set tdm slot\n"); - return ret; - } - } - - if (!strcmp(codec_dai->component->name, "i2c-10EC1015:01")) { - ret = snd_soc_dai_set_tdm_slot(codec_dai, - 0x0, 0x2, 4, 24); - if (ret < 0) { - dev_err(card->dev, "failed to set tdm slot\n"); - return ret; - } - } - } - } - - return 0; -} - -static struct snd_soc_ops sof_rt1015_ops = { - .hw_params = sof_rt1015_hw_params, -}; - static struct snd_soc_dai_link_component platform_component[] = { { /* name might be overridden during probe */ @@ -486,6 +457,7 @@ static int sof_card_late_probe(struct snd_soc_card *card) if (err < 0) return err; } + return hdac_hdmi_jack_port_init(component, &card->dapm); } @@ -517,22 +489,11 @@ static const struct snd_soc_dapm_route sof_map[] = { { "IN1P", NULL, "Headset Mic" }, }; -static const struct snd_soc_dapm_route speaker_map_lr[] = { - { "Left Spk", NULL, "Left SPO" }, - { "Right Spk", NULL, "Right SPO" }, -}; - static const struct snd_soc_dapm_route dmic_map[] = { /* digital mics */ {"DMic", NULL, "SoC DMIC"}, }; -static int speaker_codec_init_lr(struct snd_soc_pcm_runtime *rtd) -{ - return snd_soc_dapm_add_routes(&rtd->card->dapm, speaker_map_lr, - ARRAY_SIZE(speaker_map_lr)); -} - static int dmic_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -555,17 +516,6 @@ static int dmic_init(struct snd_soc_pcm_runtime *rtd) return ret; } -static struct snd_soc_codec_conf rt1015_amp_conf[] = { - { - .dlc = COMP_CODEC_CONF("i2c-10EC1015:00"), - .name_prefix = "Left", - }, - { - .dlc = COMP_CODEC_CONF("i2c-10EC1015:01"), - .name_prefix = "Right", - }, -}; - /* sof audio machine driver for rt5682 codec */ static struct snd_soc_card sof_audio_card_rt5682 = { .name = "rt5682", /* the sof- prefix is added by the core */ @@ -601,17 +551,6 @@ static struct snd_soc_dai_link_component dmic_component[] = { } }; -static struct snd_soc_dai_link_component rt1015_components[] = { - { - .name = "i2c-10EC1015:00", - .dai_name = "rt1015-aif", - }, - { - .name = "i2c-10EC1015:01", - .dai_name = "rt1015-aif", - }, -}; - static struct snd_soc_dai_link_component dummy_component[] = { { .name = "snd-soc-dummy", @@ -764,10 +703,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, links[id].id = id; if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) { - links[id].codecs = rt1015_components; - links[id].num_codecs = ARRAY_SIZE(rt1015_components); - links[id].init = speaker_codec_init_lr; - links[id].ops = &sof_rt1015_ops; + sof_rt1015_dai_link(&links[id], (sof_rt5682_quirk & + SOF_RT1015_SPEAKER_AMP_100FS) ? 100 : 64); } else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) { sof_rt1015p_dai_link(&links[id]); } else if (sof_rt5682_quirk & @@ -784,6 +721,20 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, } else if (sof_rt5682_quirk & SOF_RT1011_SPEAKER_AMP_PRESENT) { sof_rt1011_dai_link(&links[id]); + } else if (sof_rt5682_quirk & + SOF_MAX98390_SPEAKER_AMP_PRESENT) { + if (sof_rt5682_quirk & + SOF_MAX98390_TWEETER_SPEAKER_PRESENT) { + links[id].codecs = max_98390_4spk_components; + links[id].num_codecs = ARRAY_SIZE(max_98390_4spk_components); + } else { + links[id].codecs = max_98390_components; + links[id].num_codecs = ARRAY_SIZE(max_98390_components); + } + links[id].init = max_98390_spk_codec_init; + links[id].ops = &max_98390_ops; + links[id].dpcm_capture = 1; + } else { max_98357a_dai_link(&links[id]); } @@ -868,6 +819,10 @@ static int sof_audio_probe(struct platform_device *pdev) if (acpi_dev_present("RTL5682", NULL, -1)) sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT; + /* Detect the headset codec variant to support machines in DMI quirk */ + if (acpi_dev_present("RTL5682", NULL, -1)) + sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT; + if (soc_intel_is_byt() || soc_intel_is_cht()) { is_legacy_cpu = 1; dmic_be_num = 0; @@ -924,6 +879,14 @@ static int sof_audio_probe(struct platform_device *pdev) sof_rt1011_codec_conf(&sof_audio_card_rt5682); else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) sof_rt1015p_codec_conf(&sof_audio_card_rt5682); + else if (sof_rt5682_quirk & SOF_MAX98390_SPEAKER_AMP_PRESENT) { + if (sof_rt5682_quirk & SOF_MAX98390_TWEETER_SPEAKER_PRESENT) + max_98390_set_codec_conf(&sof_audio_card_rt5682, + ARRAY_SIZE(max_98390_4spk_components)); + else + max_98390_set_codec_conf(&sof_audio_card_rt5682, + ARRAY_SIZE(max_98390_components)); + } if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) sof_audio_card_rt5682.num_links++; @@ -935,10 +898,8 @@ static int sof_audio_probe(struct platform_device *pdev) sof_audio_card_rt5682.dai_link = dai_links; - if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) { - sof_audio_card_rt5682.codec_conf = rt1015_amp_conf; - sof_audio_card_rt5682.num_configs = ARRAY_SIZE(rt1015_amp_conf); - } + if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) + sof_rt1015_codec_conf(&sof_audio_card_rt5682); INIT_LIST_HEAD(&ctx->hdmi_pcm_list); @@ -1051,6 +1012,17 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_NUM_HDMIDEV(4)), }, { + .name = "adl_max98390_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98390_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, + { .name = "adl_mx98360_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | @@ -1080,6 +1052,7 @@ MODULE_DESCRIPTION("SOF Audio Machine driver"); MODULE_AUTHOR("Bard Liao <bard.liao@intel.com>"); MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>"); MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); +MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 77219c3f8766..da515eb1ddbe 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -36,8 +36,6 @@ static void log_quirks(struct device *dev) if (SOF_SSP_GET_PORT(sof_sdw_quirk)) dev_dbg(dev, "SSP port %ld\n", SOF_SSP_GET_PORT(sof_sdw_quirk)); - if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) - dev_dbg(dev, "quirk SOF_RT715_DAI_ID_FIX enabled\n"); if (sof_sdw_quirk & SOF_SDW_NO_AGGREGATION) dev_dbg(dev, "quirk SOF_SDW_NO_AGGREGATION enabled\n"); } @@ -64,8 +62,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") }, - .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + .driver_data = (void *)RT711_JD2, }, { /* early version of SKU 09C6 */ @@ -74,8 +71,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") }, - .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + .driver_data = (void *)RT711_JD2, }, { .callback = sof_sdw_quirk_cb, @@ -84,7 +80,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), }, .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -94,7 +89,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), }, .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, /* IceLake devices */ @@ -126,8 +120,17 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E") }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | - RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + RT711_JD2), + }, + { + /* another SKU of Dell Latitude 9520 */ + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2), }, { /* Dell XPS 9710 */ @@ -138,7 +141,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -149,7 +151,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -188,7 +189,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | SOF_SDW_PCH_DMIC | - RT711_JD2), + RT711_JD1), }, { /* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */ @@ -210,7 +211,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -220,8 +220,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45") }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | - RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + RT711_JD2), }, /* AlderLake devices */ { @@ -232,7 +231,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(RT711_JD2_100K | SOF_SDW_TGL_HDMI | - SOF_RT715_DAI_ID_FIX | SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, @@ -349,7 +347,7 @@ int sdw_prepare(struct snd_pcm_substream *substream) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s", dai->name); @@ -369,7 +367,7 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s", dai->name); @@ -408,7 +406,7 @@ int sdw_hw_free(struct snd_pcm_substream *substream) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s", dai->name); @@ -431,26 +429,13 @@ static const struct snd_soc_ops sdw_ops = { .shutdown = sdw_shutdown, }; -static int sof_sdw_mic_codec_mockup_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * force DAI link to use same ID as RT715 and DMIC - * to reuse topologies - */ - dai_links->id = SDW_DMIC_DAI_ID; - return 0; -} - static struct sof_sdw_codec_info codec_info_list[] = { { .part_id = 0x700, .direction = {true, true}, .dai_name = "rt700-aif1", .init = sof_sdw_rt700_init, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x711, @@ -459,6 +444,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt711-sdca-aif1", .init = sof_sdw_rt711_sdca_init, .exit = sof_sdw_rt711_sdca_exit, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x711, @@ -467,6 +453,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt711-aif1", .init = sof_sdw_rt711_init, .exit = sof_sdw_rt711_exit, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x1308, @@ -475,12 +462,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt1308-aif", .ops = &sof_sdw_rt1308_i2s_ops, .init = sof_sdw_rt1308_init, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x1316, .direction = {true, true}, .dai_name = "rt1316-aif", .init = sof_sdw_rt1316_init, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x714, @@ -489,6 +478,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_sdca_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x715, @@ -497,6 +487,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_sdca_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x714, @@ -505,6 +496,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x715, @@ -513,6 +505,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x8373, @@ -520,12 +513,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "max98373-aif1", .init = sof_sdw_mx8373_init, .codec_card_late_probe = sof_sdw_mx8373_late_probe, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x5682, .direction = {true, true}, .dai_name = "rt5682-sdw", .init = sof_sdw_rt5682_init, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0xaaaa, /* generic codec mockup */ @@ -533,6 +528,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, true}, .dai_name = "sdw-mockup-aif1", .init = NULL, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0xaa55, /* headset codec mockup */ @@ -540,6 +536,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, true}, .dai_name = "sdw-mockup-aif1", .init = NULL, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x55aa, /* amplifier mockup */ @@ -547,13 +544,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, false}, .dai_name = "sdw-mockup-aif1", .init = NULL, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x5555, .version_id = 0, .direction = {false, true}, .dai_name = "sdw-mockup-aif1", - .init = sof_sdw_mic_codec_mockup_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, }; @@ -601,10 +599,11 @@ static inline int find_codec_info_acpi(const u8 *acpi_id) * Since some sdw slaves may be aggregated, the CPU DAI number * may be larger than the number of BE dailinks. */ -static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links, +static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_link_adr *links, int *sdw_be_num, int *sdw_cpu_dai_num) { const struct snd_soc_acpi_link_adr *link; + int _codec_type = SOF_SDW_CODEC_TYPE_JACK; bool group_visited[SDW_MAX_GROUPS]; bool no_aggregation; int i; @@ -630,6 +629,12 @@ static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links, if (codec_index < 0) return codec_index; + if (codec_info_list[codec_index].codec_type < _codec_type) + dev_warn(dev, + "Unexpected address table ordering. Expected order: jack -> amp -> mic\n"); + + _codec_type = codec_info_list[codec_index].codec_type; + endpoint = link->adr_d->endpoints; /* count DAI number for playback and capture */ @@ -888,14 +893,14 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, } static int create_sdw_dailink(struct snd_soc_card *card, - struct device *dev, int *be_index, + struct device *dev, int *link_index, struct snd_soc_dai_link *dai_links, int sdw_be_num, int sdw_cpu_dai_num, struct snd_soc_dai_link_component *cpus, const struct snd_soc_acpi_link_adr *link, int *cpu_id, bool *group_generated, struct snd_soc_codec_conf *codec_conf, - int codec_count, + int codec_count, int *link_id, int *codec_conf_index, bool *ignore_pch_dmic) { @@ -953,6 +958,19 @@ static int create_sdw_dailink(struct snd_soc_card *card, if (codec_info_list[codec_index].ignore_pch_dmic) *ignore_pch_dmic = true; + /* Shift the first amplifier's *link_id to SDW_AMP_DAI_ID */ + if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_AMP && + *link_id < SDW_AMP_DAI_ID) + *link_id = SDW_AMP_DAI_ID; + + /* + * DAI ID is fixed at SDW_DMIC_DAI_ID for MICs to + * keep sdw DMIC and HDMI setting static in UCM + */ + if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_MIC && + *link_id < SDW_DMIC_DAI_ID) + *link_id = SDW_DMIC_DAI_ID; + cpu_dai_index = *cpu_id; for_each_pcm_streams(stream) { char *name, *cpu_name; @@ -991,8 +1009,12 @@ static int create_sdw_dailink(struct snd_soc_card *card, cpus[cpu_dai_index++].dai_name = cpu_name; } - if (*be_index >= sdw_be_num) { - dev_err(dev, " invalid be dai index %d", *be_index); + /* + * We create sdw dai links at first stage, so link index should + * not be larger than sdw_be_num + */ + if (*link_index >= sdw_be_num) { + dev_err(dev, "invalid dai link index %d", *link_index); return -EINVAL; } @@ -1003,18 +1025,19 @@ static int create_sdw_dailink(struct snd_soc_card *card, playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); capture = (stream == SNDRV_PCM_STREAM_CAPTURE); - init_dai_link(dev, dai_links + *be_index, *be_index, name, + init_dai_link(dev, dai_links + *link_index, (*link_id)++, name, playback, capture, cpus + *cpu_id, cpu_dai_num, codecs, codec_num, NULL, &sdw_ops); + /* * SoundWire DAILINKs use 'stream' functions and Bank Switch operations * based on wait_for_completion(), tag them as 'nonatomic'. */ - dai_links[*be_index].nonatomic = true; + dai_links[*link_index].nonatomic = true; - ret = set_codec_init_func(card, link, dai_links + (*be_index)++, + ret = set_codec_init_func(card, link, dai_links + (*link_index)++, playback, group_id); if (ret < 0) { dev_err(dev, "failed to init codec %d", codec_index); @@ -1028,17 +1051,6 @@ static int create_sdw_dailink(struct snd_soc_card *card, return 0; } -/* - * DAI link ID of SSP & DMIC & HDMI are based on last - * link ID used by sdw link. Since be_id may be changed - * in init func of sdw codec, it is not equal to be_id - */ -static inline int get_next_be_id(struct snd_soc_dai_link *links, - int be_id) -{ - return links[be_id - 1].id + 1; -} - #define IDISP_CODEC_MASK 0x4 static int sof_card_codec_conf_alloc(struct device *dev, @@ -1095,7 +1107,7 @@ static int sof_card_dai_links_create(struct device *dev, bool group_generated[SDW_MAX_GROUPS]; int ssp_codec_index, ssp_mask; struct snd_soc_dai_link *links; - int num_links, link_id = 0; + int num_links, link_index = 0; char *name, *cpu_name; int total_cpu_dai_num; int sdw_cpu_dai_num; @@ -1131,7 +1143,7 @@ static int sof_card_dai_links_create(struct device *dev, ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0; comp_num = hdmi_num + ssp_num; - ret = get_sdw_dailink_info(mach_params->links, + ret = get_sdw_dailink_info(dev, mach_params->links, &sdw_be_num, &sdw_cpu_dai_num); if (ret < 0) { dev_err(dev, "failed to get sdw link info %d", ret); @@ -1195,24 +1207,18 @@ static int sof_card_dai_links_create(struct device *dev, group_generated[endpoint->group_id]) continue; - ret = create_sdw_dailink(card, dev, &be_id, links, sdw_be_num, + ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num, sdw_cpu_dai_num, cpus, adr_link, &cpu_id, group_generated, codec_conf, codec_conf_count, - &codec_conf_index, + &be_id, &codec_conf_index, &ignore_pch_dmic); if (ret < 0) { - dev_err(dev, "failed to create dai link %d", be_id); - return -ENOMEM; + dev_err(dev, "failed to create dai link %d", link_index); + return ret; } } - /* non-sdw DAI follows sdw DAI */ - link_id = be_id; - - /* get BE ID for non-sdw DAI */ - be_id = get_next_be_id(links, be_id); - SSP: /* SSP */ if (!ssp_num) @@ -1252,17 +1258,17 @@ SSP: playback = info->direction[SNDRV_PCM_STREAM_PLAYBACK]; capture = info->direction[SNDRV_PCM_STREAM_CAPTURE]; - init_dai_link(dev, links + link_id, be_id, name, + init_dai_link(dev, links + link_index, be_id, name, playback, capture, cpus + cpu_id, 1, ssp_components, 1, NULL, info->ops); - ret = info->init(card, NULL, links + link_id, info, 0); + ret = info->init(card, NULL, links + link_index, info, 0); if (ret < 0) return ret; - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); } DMIC: @@ -1273,21 +1279,21 @@ DMIC: goto HDMI; } cpus[cpu_id].dai_name = "DMIC01 Pin"; - init_dai_link(dev, links + link_id, be_id, "dmic01", + init_dai_link(dev, links + link_index, be_id, "dmic01", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, sof_sdw_dmic_init, NULL); - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); cpus[cpu_id].dai_name = "DMIC16k Pin"; - init_dai_link(dev, links + link_id, be_id, "dmic16k", + init_dai_link(dev, links + link_index, be_id, "dmic16k", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, /* don't call sof_sdw_dmic_init() twice */ NULL, NULL); - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); } HDMI: @@ -1325,12 +1331,12 @@ HDMI: return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_id, be_id, name, + init_dai_link(dev, links + link_index, be_id, name, 1, 0, // HDMI only supports playback cpus + cpu_id, 1, idisp_components + i, 1, sof_sdw_hdmi_init, NULL); - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); } if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { @@ -1354,7 +1360,7 @@ HDMI: return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_id, be_id, name, 1, 1, + init_dai_link(dev, links + link_index, be_id, name, 1, 1, cpus + cpu_id, 1, ssp_components, 1, NULL, NULL); } diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index b35f5a9b96f5..e2457738a332 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -15,6 +15,7 @@ #define MAX_NO_PROPS 2 #define MAX_HDMI_NUM 4 +#define SDW_AMP_DAI_ID 2 #define SDW_DMIC_DAI_ID 4 #define SDW_MAX_CPU_DAIS 16 #define SDW_INTEL_BIDIR_PDI_BASE 2 @@ -42,7 +43,6 @@ enum { #define SOF_SDW_PCH_DMIC BIT(6) #define SOF_SSP_PORT(x) (((x) & GENMASK(5, 0)) << 7) #define SOF_SSP_GET_PORT(quirk) (((quirk) >> 7) & GENMASK(5, 0)) -#define SOF_RT715_DAI_ID_FIX BIT(13) #define SOF_SDW_NO_AGGREGATION BIT(14) /* BT audio offload: reserve 3 bits for future */ @@ -52,9 +52,14 @@ enum { (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(18) +#define SOF_SDW_CODEC_TYPE_JACK 0 +#define SOF_SDW_CODEC_TYPE_AMP 1 +#define SOF_SDW_CODEC_TYPE_MIC 2 + struct sof_sdw_codec_info { const int part_id; const int version_id; + const int codec_type; int amp_num; const u8 acpi_id[ACPI_ID_LEN]; const bool direction[2]; // playback & capture support diff --git a/sound/soc/intel/boards/sof_sdw_rt715.c b/sound/soc/intel/boards/sof_sdw_rt715.c index c8af3780cbc3..7c068dc6b9cf 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715.c +++ b/sound/soc/intel/boards/sof_sdw_rt715.c @@ -30,13 +30,6 @@ int sof_sdw_rt715_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - /* - * DAI ID is fixed at SDW_DMIC_DAI_ID for 715 to - * keep sdw DMIC and HDMI setting static in UCM - */ - if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) - dai_links->id = SDW_DMIC_DAI_ID; - dai_links->init = rt715_rtd_init; return 0; diff --git a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c index 85d3d8c355cc..ca0cf3db2e4d 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c @@ -30,13 +30,6 @@ int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - /* - * DAI ID is fixed at SDW_DMIC_DAI_ID for 715-SDCA to - * keep sdw DMIC and HDMI setting static in UCM - */ - if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) - dai_links->id = SDW_DMIC_DAI_ID; - dai_links->init = rt715_sdca_rtd_init; return 0; diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c index 9c5fd18f2600..346bec000306 100644 --- a/sound/soc/intel/catpt/dsp.c +++ b/sound/soc/intel/catpt/dsp.c @@ -65,6 +65,7 @@ static int catpt_dma_memcpy(struct catpt_dev *cdev, struct dma_chan *chan, { struct dma_async_tx_descriptor *desc; enum dma_status status; + int ret; desc = dmaengine_prep_dma_memcpy(chan, dst_addr, src_addr, size, DMA_CTRL_ACK); @@ -77,13 +78,22 @@ static int catpt_dma_memcpy(struct catpt_dev *cdev, struct dma_chan *chan, catpt_updatel_shim(cdev, HMDC, CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id), CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id)); - dmaengine_submit(desc); + + ret = dma_submit_error(dmaengine_submit(desc)); + if (ret) { + dev_err(cdev->dev, "submit tx failed: %d\n", ret); + goto clear_hdda; + } + status = dma_wait_for_async_tx(desc); + ret = (status == DMA_COMPLETE) ? 0 : -EPROTO; + +clear_hdda: /* regardless of status, disable access to HOST memory in demand mode */ catpt_updatel_shim(cdev, HMDC, CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id), 0); - return (status == DMA_COMPLETE) ? 0 : -EPROTO; + return ret; } int catpt_dma_memcpy_todsp(struct catpt_dev *cdev, struct dma_chan *chan, diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index ebb27daeb1c7..939a9b801dec 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -259,9 +259,9 @@ static enum catpt_channel_config catpt_get_channel_config(u32 num_channels) static int catpt_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_template *template; struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct resource *res; int ret; @@ -306,8 +306,8 @@ err_pgtbl: static void catpt_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); stream = snd_soc_dai_get_dma_data(dai, substream); @@ -329,9 +329,9 @@ 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_dev *cdev = dev_get_drvdata(dai->dev); 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; @@ -374,12 +374,12 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct snd_pcm_runtime *rtm = substream->runtime; + struct snd_dma_buffer *dmab; struct catpt_stream_runtime *stream; struct catpt_audio_format afmt; struct catpt_ring_info rinfo; - struct snd_pcm_runtime *rtm = substream->runtime; - struct snd_dma_buffer *dmab; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; stream = snd_soc_dai_get_dma_data(dai, substream); @@ -427,8 +427,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, static int catpt_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); stream = snd_soc_dai_get_dma_data(dai, substream); if (!stream->allocated) @@ -444,8 +444,8 @@ static int catpt_dai_hw_free(struct snd_pcm_substream *substream, static int catpt_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; stream = snd_soc_dai_get_dma_data(dai, substream); @@ -467,9 +467,9 @@ static int catpt_dai_prepare(struct snd_pcm_substream *substream, static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); - struct catpt_stream_runtime *stream; struct snd_pcm_runtime *runtime = substream->runtime; + struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); snd_pcm_uframes_t pos; int ret; @@ -595,9 +595,8 @@ static int catpt_component_open(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtm = substream->private_data; - if (rtm->dai_link->no_pcm) - return 0; - snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); + if (!rtm->dai_link->no_pcm) + snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); return 0; } @@ -605,10 +604,10 @@ static snd_pcm_uframes_t catpt_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - struct catpt_stream_runtime *stream; struct snd_soc_pcm_runtime *rtm = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0); + struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); u32 pos; if (rtm->dai_link->no_pcm) @@ -633,8 +632,8 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, struct snd_soc_dai *dai) { struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtm, 0); - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_ssp_device_format devfmt; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; devfmt.iface = dai->driver->id; @@ -894,8 +893,8 @@ static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); long *ctlvol = (long *)kcontrol->private_value; u32 dspvol; int i; @@ -926,8 +925,8 @@ static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); long *ctlvol = (long *)kcontrol->private_value; int ret, i; @@ -1002,8 +1001,8 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); bool mute; int ret; diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index b61a778a9d26..f32bcb2b2e09 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -374,6 +374,16 @@ static const struct snd_soc_acpi_codecs adl_rt5682_rt5682s_hp = { .codecs = {"10EC5682", "RTL5682"}, }; +static const struct snd_soc_acpi_codecs adl_rt1019p_amp = { + .num_codecs = 1, + .codecs = {"RTL1019"} +}; + +static const struct snd_soc_acpi_codecs adl_max98390_amp = { + .num_codecs = 1, + .codecs = {"MX98390"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { { .comp_ids = &adl_rt5682_rt5682s_hp, @@ -399,6 +409,44 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-max98360a-rt5682.tplg", }, + { + .id = "10508825", + .drv_name = "adl_rt1019p_nau8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_rt1019p_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-rt1019-nau8825.tplg", + }, + { + .id = "10508825", + .drv_name = "adl_max98373_nau8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98373_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-max98373-nau8825.tplg", + }, + { + .id = "10508825", + .drv_name = "adl_mx98360a_nau8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98360a_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-mx98360a-nau8825.tplg", + }, + { + .id = "10508825", + .drv_name = "sof_nau8825", + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-nau8825.tplg", + }, + { + .comp_ids = &adl_rt5682_rt5682s_hp, + .drv_name = "adl_max98390_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98390_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-max98390-rt5682.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines); diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 64226072f0ee..2439a574ac2f 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -13,108 +13,6 @@ #include "skl.h" #include "skl-i2s.h" -static struct nhlt_specific_cfg *skl_get_specific_cfg( - struct device *dev, struct nhlt_fmt *fmt, - u8 no_ch, u32 rate, u16 bps, u8 linktype) -{ - struct nhlt_specific_cfg *sp_config; - struct wav_fmt *wfmt; - struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config; - int i; - - dev_dbg(dev, "Format count =%d\n", fmt->fmt_count); - - for (i = 0; i < fmt->fmt_count; i++) { - wfmt = &fmt_config->fmt_ext.fmt; - dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, - wfmt->bits_per_sample, wfmt->samples_per_sec); - if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) { - /* - * if link type is dmic ignore rate check as the blob is - * generic for all rates - */ - sp_config = &fmt_config->config; - if (linktype == NHLT_LINK_DMIC) - return sp_config; - - if (wfmt->samples_per_sec == rate) - return sp_config; - } - - fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + - fmt_config->config.size); - } - - return NULL; -} - -static void dump_config(struct device *dev, u32 instance_id, u8 linktype, - u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps) -{ - dev_dbg(dev, "Input configuration\n"); - dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate); - dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype); - dev_dbg(dev, "bits_per_sample=%d\n", bps); -} - -static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, - u32 instance_id, u8 link_type, u8 dirn, u8 dev_type) -{ - dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n", - epnt->virtual_bus_id, epnt->linktype, - epnt->direction, epnt->device_type); - - if ((epnt->virtual_bus_id == instance_id) && - (epnt->linktype == link_type) && - (epnt->direction == dirn)) { - /* do not check dev_type for DMIC link type */ - if (epnt->linktype == NHLT_LINK_DMIC) - return true; - - if (epnt->device_type == dev_type) - return true; - } - - return false; -} - -struct nhlt_specific_cfg -*skl_get_ep_blob(struct skl_dev *skl, u32 instance, u8 link_type, - u8 s_fmt, u8 num_ch, u32 s_rate, - u8 dirn, u8 dev_type) -{ - struct nhlt_fmt *fmt; - struct nhlt_endpoint *epnt; - struct hdac_bus *bus = skl_to_bus(skl); - struct device *dev = bus->dev; - struct nhlt_specific_cfg *sp_config; - struct nhlt_acpi_table *nhlt = skl->nhlt; - u16 bps = (s_fmt == 16) ? 16 : 32; - u8 j; - - dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); - - epnt = (struct nhlt_endpoint *)nhlt->desc; - - dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count); - - for (j = 0; j < nhlt->endpoint_count; j++) { - if (skl_check_ep_match(dev, epnt, instance, link_type, - dirn, dev_type)) { - fmt = (struct nhlt_fmt *)(epnt->config.caps + - epnt->config.size); - sp_config = skl_get_specific_cfg(dev, fmt, num_ch, - s_rate, bps, link_type); - if (sp_config) - return sp_config; - } - - epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); - } - - return NULL; -} - static void skl_nhlt_trim_space(char *trim) { char *s = trim; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 9ecaf6a1e847..55f310e91b55 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -317,6 +317,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "dma_id=%d\n", dma_id); p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.s_cont = snd_pcm_format_physical_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.host_dma_id = dma_id; @@ -405,6 +406,7 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, struct skl_pipe_params p_params = {0}; p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.s_cont = snd_pcm_format_physical_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; @@ -562,13 +564,11 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, stream_tag = hdac_stream(link_dev)->stream_tag; - /* set the stream tag in the codec dai dma params */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); - else - snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); + /* set the hdac_stream in the codec dai */ + snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream); p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.s_cont = snd_pcm_format_physical_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; @@ -1251,7 +1251,6 @@ static int skl_platform_soc_get_time_info( snd_pcm_gettime(substream->runtime, system_ts); nsec = timecounter_read(&hstr->tc); - nsec = div_u64(nsec, 3); /* can be optimized */ if (audio_tstamp_config->report_delay) nsec = skl_adjust_codec_delay(substream, nsec); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 89e4231304dd..9bdf020a2b64 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -285,7 +285,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, { struct skl_module_cfg *m_cfg = w->priv; int link_type, dir; - u32 ch, s_freq, s_fmt; + u32 ch, s_freq, s_fmt, s_cont; struct nhlt_specific_cfg *cfg; u8 dev_type = skl_tplg_be_dev_type(m_cfg->dev_type); int fmt_idx = m_cfg->fmt_idx; @@ -301,7 +301,8 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, link_type = NHLT_LINK_DMIC; dir = SNDRV_PCM_STREAM_CAPTURE; s_freq = m_iface->inputs[0].fmt.s_freq; - s_fmt = m_iface->inputs[0].fmt.bit_depth; + s_fmt = m_iface->inputs[0].fmt.valid_bit_depth; + s_cont = m_iface->inputs[0].fmt.bit_depth; ch = m_iface->inputs[0].fmt.channels; break; @@ -310,12 +311,14 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) { dir = SNDRV_PCM_STREAM_PLAYBACK; s_freq = m_iface->outputs[0].fmt.s_freq; - s_fmt = m_iface->outputs[0].fmt.bit_depth; + s_fmt = m_iface->outputs[0].fmt.valid_bit_depth; + s_cont = m_iface->outputs[0].fmt.bit_depth; ch = m_iface->outputs[0].fmt.channels; } else { dir = SNDRV_PCM_STREAM_CAPTURE; s_freq = m_iface->inputs[0].fmt.s_freq; - s_fmt = m_iface->inputs[0].fmt.bit_depth; + s_fmt = m_iface->inputs[0].fmt.valid_bit_depth; + s_cont = m_iface->inputs[0].fmt.bit_depth; ch = m_iface->inputs[0].fmt.channels; } break; @@ -325,16 +328,17 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, } /* update the blob based on virtual bus_id and default params */ - cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type, - s_fmt, ch, s_freq, dir, dev_type); + cfg = intel_nhlt_get_endpoint_blob(skl->dev, skl->nhlt, m_cfg->vbus_id, + link_type, s_fmt, s_cont, ch, + s_freq, dir, dev_type); if (cfg) { m_cfg->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; m_cfg->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps; } else { dev_err(skl->dev, "Blob NULL for id %x type %d dirn %d\n", m_cfg->vbus_id, link_type, dir); - dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d\n", - ch, s_freq, s_fmt); + dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d/%d\n", + ch, s_freq, s_fmt, s_cont); return -EIO; } @@ -1849,10 +1853,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, pipe_fmt = &pipe->configs[pipe->pipe_config_idx].in_fmt; /* update the blob based on virtual bus_id*/ - cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, - pipe_fmt->bps, pipe_fmt->channels, - pipe_fmt->freq, pipe->direction, - dev_type); + cfg = intel_nhlt_get_endpoint_blob(dai->dev, skl->nhlt, + mconfig->vbus_id, link_type, + pipe_fmt->bps, params->s_cont, + pipe_fmt->channels, pipe_fmt->freq, + pipe->direction, dev_type); if (cfg) { mconfig->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; mconfig->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index f0695b2ac5dd..22963634fbea 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -284,6 +284,7 @@ struct skl_pipe_params { u32 ch; u32 s_freq; u32 s_fmt; + u32 s_cont; u8 linktype; snd_pcm_format_t format; int link_index; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 5b1a15e39912..148ddf4cace0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -439,7 +439,7 @@ static int skl_free(struct hdac_bus *bus) skl->init_done = 0; /* to be sure */ - snd_hdac_ext_stop_streams(bus); + snd_hdac_stop_streams_and_chip(bus); if (bus->irq >= 0) free_irq(bus->irq, (void *)bus); @@ -1096,7 +1096,7 @@ static void skl_shutdown(struct pci_dev *pci) if (!skl->init_done) return; - snd_hdac_ext_stop_streams(bus); + snd_hdac_stop_streams_and_chip(bus); list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); snd_hdac_ext_stream_decouple(bus, stream, false); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 33ed274fc0cb..f55f8b3dbdc3 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -165,10 +165,6 @@ struct skl_dsp_ops { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); -struct nhlt_specific_cfg *skl_get_ep_blob(struct skl_dev *skl, u32 instance, - u8 link_type, u8 s_fmt, u8 num_ch, - u32 s_rate, u8 dirn, u8 dev_type); - int skl_nhlt_update_topology_bin(struct skl_dev *skl); int skl_init_dsp(struct skl_dev *skl); int skl_free_dsp(struct skl_dev *skl); diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 3b1ddea26a9e..9306b7ca2644 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -205,6 +205,7 @@ config SND_SOC_MT8195_MT6359_RT1019_RT5682 select SND_SOC_MT6359 select SND_SOC_RT1015P select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_DMIC select SND_SOC_HDMI_CODEC help @@ -220,6 +221,7 @@ config SND_SOC_MT8195_MT6359_RT1011_RT5682 select SND_SOC_MT6359 select SND_SOC_RT1011 select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_DMIC select SND_SOC_HDMI_CODEC help diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index bc3d0466472b..0f178de92a0f 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -1474,9 +1474,7 @@ static struct platform_driver mt2701_afe_pcm_driver = { .driver = { .name = "mt2701-audio", .of_match_table = mt2701_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt2701_afe_pm_ops, -#endif }, .probe = mt2701_afe_pcm_dev_probe, .remove = mt2701_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 3d68e4726ea2..fb4abec9aa5f 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -901,9 +901,7 @@ static struct platform_driver mt6797_afe_pcm_driver = { .driver = { .name = "mt6797-audio", .of_match_table = mt6797_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt6797_afe_pm_ops, -#endif }, .probe = mt6797_afe_pcm_dev_probe, .remove = mt6797_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index fc94314bfc02..4cb90da89262 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -177,9 +177,9 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); + + of_node_put(codec_node); + of_node_put(platform_node); return ret; } @@ -193,9 +193,7 @@ static struct platform_driver mt8173_max98090_driver = { .driver = { .name = "mt8173-max98090", .of_match_table = mt8173_max98090_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_max98090_dev_probe, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 0f28dc2217c0..b55122b99f07 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -215,9 +215,8 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); + + of_node_put(platform_node); return ret; } @@ -231,9 +230,7 @@ static struct platform_driver mt8173_rt5650_rt5514_driver = { .driver = { .name = "mtk-rt5650-rt5514", .of_match_table = mt8173_rt5650_rt5514_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_rt5650_rt5514_dev_probe, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 077c6ee06780..5716d9299066 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -282,9 +282,8 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); + + of_node_put(platform_node); return ret; } @@ -298,9 +297,7 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = { .driver = { .name = "mtk-rt5650-rt5676", .of_match_table = mt8173_rt5650_rt5676_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_rt5650_rt5676_dev_probe, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 2cbf679f5c74..fc164f4f95f8 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -320,9 +320,8 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); + + of_node_put(platform_node); return ret; } @@ -336,9 +335,7 @@ static struct platform_driver mt8173_rt5650_driver = { .driver = { .name = "mtk-rt5650", .of_match_table = mt8173_rt5650_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_rt5650_dev_probe, }; diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 14e77df06b01..86c8a523fe9e 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -1279,9 +1279,7 @@ static struct platform_driver mt8183_afe_pcm_driver = { .driver = { .name = "mt8183-audio", .of_match_table = mt8183_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt8183_afe_pm_ops, -#endif }, .probe = mt8183_afe_pcm_dev_probe, .remove = mt8183_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index a4d26a6fc849..718505c75418 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -155,9 +155,9 @@ static const struct snd_soc_ops mt8183_da7219_rt1015_i2s_ops = { static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); @@ -167,9 +167,9 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); @@ -685,7 +685,6 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) struct snd_soc_dai_link *dai_link; struct mt8183_da7219_max98357_priv *priv; struct pinctrl *pinctrl; - const struct of_device_id *match; int ret, i; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -695,11 +694,9 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) return -EINVAL; } - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; - - card = (struct snd_soc_card *)match->data; card->dev = &pdev->dev; hdmi_codec = of_parse_phandle(pdev->dev.of_node, @@ -781,7 +778,11 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) return ret; } - return devm_snd_soc_register_card(&pdev->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); + + of_node_put(platform_node); + of_node_put(hdmi_codec); + return ret; } #ifdef CONFIG_OF diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index aeb1af86047e..b0ec5ebd4f2d 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -94,11 +94,11 @@ static const struct snd_soc_ops mt8183_mt6358_rt1015_i2s_ops = { static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__); + dev_dbg(rtd->dev, "%s(), fix format to S32_LE\n", __func__); - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); return 0; @@ -107,11 +107,11 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__); + dev_dbg(rtd->dev, "%s(), fix format to S24_LE\n", __func__); - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; @@ -637,7 +637,6 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) struct device_node *platform_node, *ec_codec, *hdmi_codec; struct snd_soc_dai_link *dai_link; struct mt8183_mt6358_ts3a227_max98357_priv *priv; - const struct of_device_id *match; int ret, i; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -647,11 +646,9 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) return -EINVAL; } - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; - - card = (struct snd_soc_card *)match->data; card->dev = &pdev->dev; ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0); @@ -780,7 +777,12 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) __func__, ret); } - return devm_snd_soc_register_card(&pdev->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); + + of_node_put(platform_node); + of_node_put(ec_codec); + of_node_put(hdmi_codec); + return ret; } #ifdef CONFIG_OF diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index 31c280339c50..e1e4ca931551 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -2381,9 +2381,7 @@ static struct platform_driver mt8192_afe_pcm_driver = { .driver = { .name = "mt8192-audio", .of_match_table = mt8192_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt8192_afe_pm_ops, -#endif }, .probe = mt8192_afe_pcm_dev_probe, .remove = mt8192_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index a606133951b7..f7daad1bfe1e 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -350,9 +350,9 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd) static int mt8192_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); @@ -1106,7 +1106,6 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) struct device_node *platform_node, *hdmi_codec; int ret, i; struct snd_soc_dai_link *dai_link; - const struct of_device_id *match; struct mt8192_mt6359_priv *priv; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -1116,11 +1115,9 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) return -EINVAL; } - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; - - card = (struct snd_soc_card *)match->data; card->dev = &pdev->dev; hdmi_codec = of_parse_phandle(pdev->dev.of_node, @@ -1172,7 +1169,11 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) return ret; } - return devm_snd_soc_register_card(&pdev->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); + + of_node_put(platform_node); + of_node_put(hdmi_codec); + return ret; } #ifdef CONFIG_OF diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c index 8420b2c71332..c2543f4cffb7 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c @@ -326,7 +326,7 @@ int mt8195_afe_enable_reg_rw_clk(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_SCP_ADSP_AUDIODSP, /* bus clock for infra */ MT8195_CLK_TOP_AUDIO_H_SEL, /* clock for ADSP bus */ MT8195_CLK_TOP_AUDIO_LOCAL_BUS_SEL, /* bus clock for DRAM access */ @@ -347,7 +347,7 @@ int mt8195_afe_disable_reg_rw_clk(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_AUD_A1SYS, MT8195_CLK_AUD_A1SYS_HP, MT8195_CLK_AUD_AFE, @@ -380,11 +380,11 @@ static int mt8195_afe_enable_timing_sys(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_AUD_A1SYS, MT8195_CLK_AUD_A2SYS, }; - unsigned int cg_array[] = { + static const unsigned int cg_array[] = { MT8195_TOP_CG_A1SYS_TIMING, MT8195_TOP_CG_A2SYS_TIMING, MT8195_TOP_CG_26M_TIMING, @@ -403,11 +403,11 @@ static int mt8195_afe_disable_timing_sys(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_AUD_A2SYS, MT8195_CLK_AUD_A1SYS, }; - unsigned int cg_array[] = { + static const unsigned int cg_array[] = { MT8195_TOP_CG_26M_TIMING, MT8195_TOP_CG_A2SYS_TIMING, MT8195_TOP_CG_A1SYS_TIMING, diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 2bb05a828e8d..e425f868476a 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -14,6 +14,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> #include <linux/pm_runtime.h> #include "mt8195-afe-common.h" #include "mt8195-afe-clk.h" @@ -3028,7 +3029,7 @@ static const struct reg_sequence mt8195_afe_reg_defaults[] = { static const struct reg_sequence mt8195_cg_patch[] = { { AUDIO_TOP_CON0, 0xfffffffb }, - { AUDIO_TOP_CON1, 0xfffffffa }, + { AUDIO_TOP_CON1, 0xfffffff8 }, }; static int mt8195_afe_init_registers(struct mtk_base_afe *afe) @@ -3061,6 +3062,12 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) int i, irq_id, ret; struct snd_soc_component *component; + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "failed to assign memory region: %d\n", ret); + return ret; + } + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33)); if (ret) return ret; diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c index 5d10d2c4c991..151914c873ac 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c @@ -80,8 +80,15 @@ static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = { mtk_dai_pcm_o001_mix, ARRAY_SIZE(mtk_dai_pcm_o001_mix)), + SND_SOC_DAPM_SUPPLY("PCM_EN", PCM_INTF_CON1, + PCM_INTF_CON1_PCM_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_INPUT("PCM1_INPUT"), SND_SOC_DAPM_OUTPUT("PCM1_OUTPUT"), + + SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc11"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc12"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_pcmif"), }; static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { @@ -97,22 +104,18 @@ static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { {"PCM1 Playback", NULL, "O000"}, {"PCM1 Playback", NULL, "O001"}, + {"PCM1 Playback", NULL, "PCM_EN"}, + {"PCM1 Playback", NULL, "aud_asrc12"}, + {"PCM1 Playback", NULL, "aud_pcmif"}, + + {"PCM1 Capture", NULL, "PCM_EN"}, + {"PCM1 Capture", NULL, "aud_asrc11"}, + {"PCM1 Capture", NULL, "aud_pcmif"}, + {"PCM1_OUTPUT", NULL, "PCM1 Playback"}, {"PCM1 Capture", NULL, "PCM1_INPUT"}, }; -static void mtk_dai_pcm_enable(struct mtk_base_afe *afe) -{ - regmap_update_bits(afe->regmap, PCM_INTF_CON1, - PCM_INTF_CON1_PCM_EN, PCM_INTF_CON1_PCM_EN); -} - -static void mtk_dai_pcm_disable(struct mtk_base_afe *afe) -{ - regmap_update_bits(afe->regmap, PCM_INTF_CON1, - PCM_INTF_CON1_PCM_EN, 0x0); -} - static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -207,54 +210,22 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, } /* dai ops */ -static int mtk_dai_pcm_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8195_afe_private *afe_priv = afe->platform_priv; - - if (dai->component->active) - return 0; - - mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC11]); - mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC12]); - mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_PCMIF]); - - return 0; -} - -static void mtk_dai_pcm_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8195_afe_private *afe_priv = afe->platform_priv; - - if (dai->component->active) - return; - - mtk_dai_pcm_disable(afe); - - mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_PCMIF]); - mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC12]); - mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC11]); -} - static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - int ret = 0; + int ret; - if (snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) && - snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) + dev_dbg(dai->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n", + __func__, dai->id, substream->stream, + dai->playback_widget->active, dai->capture_widget->active); + + if (dai->playback_widget->active || dai->capture_widget->active) return 0; ret = mtk_dai_pcm_configure(substream, dai); if (ret) return ret; - mtk_dai_pcm_enable(afe); - return 0; } @@ -316,8 +287,6 @@ static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } static const struct snd_soc_dai_ops mtk_dai_pcm_ops = { - .startup = mtk_dai_pcm_startup, - .shutdown = mtk_dai_pcm_shutdown, .prepare = mtk_dai_pcm_prepare, .set_fmt = mtk_dai_pcm_set_fmt, }; diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c index e103102d7ef6..5443a29da7b1 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c @@ -18,6 +18,7 @@ #include "../../codecs/rt1011.h" #include "../../codecs/rt5682.h" #include "../common/mtk-afe-platform-driver.h" +#include "mt8195-afe-clk.h" #include "mt8195-afe-common.h" #define RT1011_CODEC_DAI "rt1011-aif" @@ -27,13 +28,14 @@ #define RT5682_CODEC_DAI "rt5682-aif1" #define RT5682_DEV0_NAME "rt5682.2-001a" +#define RT5682S_CODEC_DAI "rt5682s-aif1" +#define RT5682S_DEV0_NAME "rt5682s.2-001a" + struct mt8195_mt6359_rt1011_rt5682_priv { - struct device_node *platform_node; - struct device_node *hdmi_node; - struct device_node *dp_node; struct snd_soc_jack headset_jack; struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; + struct clk *i2so1_mclk; }; static const struct snd_soc_dapm_widget @@ -84,8 +86,8 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_BCLK1, - rate * 64, rate * 512); + ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK, + rate * 256, rate * 512); if (ret) { dev_err(card->dev, "failed to set pll\n"); return ret; @@ -98,7 +100,7 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, return ret; } - return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 128, + return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, SND_SOC_CLOCK_OUT); } @@ -327,8 +329,14 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) struct mt8195_mt6359_rt1011_rt5682_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_jack *jack = &priv->headset_jack; + struct snd_soc_component *cmpnt_afe = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); + struct mt8195_afe_private *afe_priv = afe->platform_priv; int ret; + priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2]; + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | @@ -356,7 +364,7 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); @@ -421,7 +429,7 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream, SND_SOC_CLOCK_OUT); } -static struct snd_soc_ops mt8195_dptx_ops = { +static const struct snd_soc_ops mt8195_dptx_ops = { .hw_params = mt8195_dptx_hw_params, }; @@ -461,7 +469,7 @@ static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); @@ -562,6 +570,47 @@ static const struct snd_soc_ops mt8195_capture_ops = { .startup = mt8195_capture_startup, }; +static int mt8195_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + struct mt8195_mt6359_rt1011_rt5682_priv *priv = + snd_soc_card_get_drvdata(card); + int ret; + + /* + * It's required to control mclk directly in the set_bias_level_post + * function for rt5682 and rt5682s codec, or the unexpected pop happens + * at the end of playback. + */ + if (!component || + (strcmp(component->name, RT5682_DEV0_NAME) && + strcmp(component->name, RT5682S_DEV0_NAME))) + return 0; + + switch (level) { + case SND_SOC_BIAS_OFF: + if (!__clk_is_enabled(priv->i2so1_mclk)) + return 0; + + clk_disable_unprepare(priv->i2so1_mclk); + dev_dbg(card->dev, "Disable i2so1 mclk\n"); + break; + case SND_SOC_BIAS_ON: + ret = clk_prepare_enable(priv->i2so1_mclk); + if (ret) { + dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret); + return ret; + } + dev_dbg(card->dev, "Enable i2so1 mclk\n"); + break; + default: + break; + } + + return 0; +} + enum { DAI_LINK_DL2_FE, DAI_LINK_DL3_FE, @@ -691,14 +740,12 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE, SND_SOC_DAILINK_DEFS(ETDM2_IN_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE, @@ -998,6 +1045,7 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1011_rt5682_dai_links[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, .dpcm_capture = 1, SND_SOC_DAILINK_REG(PCM1_BE), }, @@ -1039,6 +1087,7 @@ static struct snd_soc_card mt8195_mt6359_rt1011_rt5682_soc_card = { .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_routes), .codec_conf = rt1011_amp_conf, .num_configs = ARRAY_SIZE(rt1011_amp_conf), + .set_bias_level_post = mt8195_set_bias_level_post, }; static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) @@ -1046,82 +1095,77 @@ static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) struct snd_soc_card *card = &mt8195_mt6359_rt1011_rt5682_soc_card; struct snd_soc_dai_link *dai_link; struct mt8195_mt6359_rt1011_rt5682_priv *priv; + struct device_node *platform_node, *dp_node, *hdmi_node; + int is5682s = 0; int ret, i; card->dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) { + dev_err(&pdev->dev, "%s new card name parsing error %d\n", + __func__, ret); + return ret; + } + + if (strstr(card->name, "_5682s")) + is5682s = 1; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->platform_node = of_parse_phandle(pdev->dev.of_node, - "mediatek,platform", 0); - if (!priv->platform_node) { + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n"); return -EINVAL; } + dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0); + hdmi_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,hdmi-codec", 0); + for_each_card_prelinks(card, i, dai_link) { if (!dai_link->platforms->name) - dai_link->platforms->of_node = priv->platform_node; + dai_link->platforms->of_node = platform_node; if (strcmp(dai_link->name, "DPTX_BE") == 0) { - priv->dp_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,dptx-codec", 0); - - if (!priv->dp_node) { + if (!dp_node) { dev_dbg(&pdev->dev, "No property 'dptx-codec'\n"); } else { - dai_link->codecs->of_node = priv->dp_node; + dai_link->codecs->of_node = dp_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_dptx_codec_init; } - } - - if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { - priv->hdmi_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,hdmi-codec", 0); - if (!priv->hdmi_node) { + } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { + if (!hdmi_node) { dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n"); } else { - dai_link->codecs->of_node = priv->hdmi_node; + dai_link->codecs->of_node = hdmi_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_hdmi_codec_init; } + } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 || + strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + dai_link->codecs->name = + is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME; + dai_link->codecs->dai_name = + is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; } } snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); - of_node_put(priv->hdmi_node); - of_node_put(priv->dp_node); - of_node_put(priv->platform_node); - } + of_node_put(platform_node); + of_node_put(dp_node); + of_node_put(hdmi_node); return ret; } -static int mt8195_mt6359_rt1011_rt5682_dev_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct mt8195_mt6359_rt1011_rt5682_priv *priv = - snd_soc_card_get_drvdata(card); - - of_node_put(priv->hdmi_node); - of_node_put(priv->dp_node); - of_node_put(priv->platform_node); - - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id mt8195_mt6359_rt1011_rt5682_dt_match[] = { {.compatible = "mediatek,mt8195_mt6359_rt1011_rt5682",}, @@ -1143,7 +1187,6 @@ static struct platform_driver mt8195_mt6359_rt1011_rt5682_driver = { .pm = &mt8195_mt6359_rt1011_rt5682_pm_ops, }, .probe = mt8195_mt6359_rt1011_rt5682_dev_probe, - .remove = mt8195_mt6359_rt1011_rt5682_dev_remove, }; module_platform_driver(mt8195_mt6359_rt1011_rt5682_driver); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index 95abaadcd842..29c2d3407cc7 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 -// -// mt8195-mt6359-rt1019-rt5682.c -- -// MT8195-MT6359-RT1019-RT6358 ALSA SoC machine driver -// -// Copyright (c) 2021 MediaTek Inc. -// Author: Trevor Wu <trevor.wu@mediatek.com> -// +/* + * mt8195-mt6359-rt1019-rt5682.c -- + * MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver + * + * Copyright (c) 2021 MediaTek Inc. + * Author: Trevor Wu <trevor.wu@mediatek.com> + * YC Hung <yc.hung@mediatek.com> + */ #include <linux/input.h> #include <linux/module.h> @@ -13,10 +14,12 @@ #include <sound/jack.h> #include <sound/pcm_params.h> #include <sound/rt5682.h> +#include <sound/sof.h> #include <sound/soc.h> #include "../../codecs/mt6359.h" #include "../../codecs/rt5682.h" #include "../common/mtk-afe-platform-driver.h" +#include "mt8195-afe-clk.h" #include "mt8195-afe-common.h" #define RT1019_CODEC_DAI "HiFi" @@ -25,13 +28,26 @@ #define RT5682_CODEC_DAI "rt5682-aif1" #define RT5682_DEV0_NAME "rt5682.2-001a" +#define RT5682S_CODEC_DAI "rt5682s-aif1" +#define RT5682S_DEV0_NAME "rt5682s.2-001a" + +#define SOF_DMA_DL2 "SOF_DMA_DL2" +#define SOF_DMA_DL3 "SOF_DMA_DL3" +#define SOF_DMA_UL4 "SOF_DMA_UL4" +#define SOF_DMA_UL5 "SOF_DMA_UL5" + +struct sof_conn_stream { + const char *normal_link; + const char *sof_link; + const char *sof_dma; + int stream_dir; +}; + struct mt8195_mt6359_rt1019_rt5682_priv { - struct device_node *platform_node; - struct device_node *hdmi_node; - struct device_node *dp_node; struct snd_soc_jack headset_jack; struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; + struct clk *i2so1_mclk; }; static const struct snd_soc_dapm_widget @@ -39,6 +55,10 @@ static const struct snd_soc_dapm_widget SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER(SOF_DMA_DL3, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER(SOF_DMA_UL4, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER(SOF_DMA_UL5, SND_SOC_NOPM, 0, 0, NULL, 0), }; static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = { @@ -48,6 +68,16 @@ static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = { { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, { "IN1P", NULL, "Headset Mic" }, + /* SOF Uplink */ + {SOF_DMA_UL4, NULL, "O034"}, + {SOF_DMA_UL4, NULL, "O035"}, + {SOF_DMA_UL5, NULL, "O036"}, + {SOF_DMA_UL5, NULL, "O037"}, + /* SOF Downlink */ + {"I070", NULL, SOF_DMA_DL2}, + {"I071", NULL, SOF_DMA_DL2}, + {"I020", NULL, SOF_DMA_DL3}, + {"I021", NULL, SOF_DMA_DL3}, }; static const struct snd_kcontrol_new mt8195_mt6359_rt1019_rt5682_controls[] = { @@ -64,8 +94,6 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int rate = params_rate(params); - unsigned int mclk_fs_ratio = 128; - unsigned int mclk_fs = rate * mclk_fs_ratio; int bitwidth; int ret; @@ -81,25 +109,22 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, - RT5682_PLL1_S_BCLK1, - params_rate(params) * 64, - params_rate(params) * 512); + ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK, + rate * 256, rate * 512); if (ret) { dev_err(card->dev, "failed to set pll\n"); return ret; } - ret = snd_soc_dai_set_sysclk(codec_dai, - RT5682_SCLK_S_PLL1, - params_rate(params) * 512, - SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + rate * 512, SND_SOC_CLOCK_IN); if (ret) { dev_err(card->dev, "failed to set sysclk\n"); return ret; } - return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT); + return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, + SND_SOC_CLOCK_OUT); } static const struct snd_soc_ops mt8195_rt5682_etdm_ops = { @@ -294,8 +319,14 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) struct mt8195_mt6359_rt1019_rt5682_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_jack *jack = &priv->headset_jack; + struct snd_soc_component *cmpnt_afe = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); + struct mt8195_afe_private *afe_priv = afe->platform_priv; int ret; + priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2]; + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | @@ -323,7 +354,7 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); @@ -391,7 +422,7 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream, SND_SOC_CLOCK_OUT); } -static struct snd_soc_ops mt8195_dptx_ops = { +static const struct snd_soc_ops mt8195_dptx_ops = { .hw_params = mt8195_dptx_hw_params, }; @@ -431,7 +462,7 @@ static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); @@ -532,6 +563,48 @@ static const struct snd_soc_ops mt8195_capture_ops = { .startup = mt8195_capture_startup, }; +static int mt8195_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + struct mt8195_mt6359_rt1019_rt5682_priv *priv = + snd_soc_card_get_drvdata(card); + int ret; + + /* + * It's required to control mclk directly in the set_bias_level_post + * function for rt5682 and rt5682s codec, or the unexpected pop happens + * at the end of playback. + */ + if (!component || + (strcmp(component->name, RT5682_DEV0_NAME) && + strcmp(component->name, RT5682S_DEV0_NAME))) + return 0; + + + switch (level) { + case SND_SOC_BIAS_OFF: + if (!__clk_is_enabled(priv->i2so1_mclk)) + return 0; + + clk_disable_unprepare(priv->i2so1_mclk); + dev_dbg(card->dev, "Disable i2so1 mclk\n"); + break; + case SND_SOC_BIAS_ON: + ret = clk_prepare_enable(priv->i2so1_mclk); + if (ret) { + dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret); + return ret; + } + dev_dbg(card->dev, "Enable i2so1 mclk\n"); + break; + default: + break; + } + + return 0; +} + enum { DAI_LINK_DL2_FE, DAI_LINK_DL3_FE, @@ -559,8 +632,17 @@ enum { DAI_LINK_PCM1_BE, DAI_LINK_UL_SRC1_BE, DAI_LINK_UL_SRC2_BE, + DAI_LINK_REGULAR_LAST = DAI_LINK_UL_SRC2_BE, + DAI_LINK_SOF_START, + DAI_LINK_SOF_DL2_BE = DAI_LINK_SOF_START, + DAI_LINK_SOF_DL3_BE, + DAI_LINK_SOF_UL4_BE, + DAI_LINK_SOF_UL5_BE, + DAI_LINK_SOF_END = DAI_LINK_SOF_UL5_BE, }; +#define DAI_LINK_REGULAR_NUM (DAI_LINK_REGULAR_LAST + 1) + /* FE */ SND_SOC_DAILINK_DEFS(DL2_FE, DAILINK_COMP_ARRAY(COMP_CPU("DL2")), @@ -661,14 +743,12 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE, SND_SOC_DAILINK_DEFS(ETDM2_IN_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE, @@ -701,6 +781,154 @@ SND_SOC_DAILINK_DEFS(UL_SRC2_BE, "mt6359-snd-codec-aif2")), DAILINK_COMP_ARRAY(COMP_EMPTY())); +SND_SOC_DAILINK_DEFS(AFE_SOF_DL2, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL2")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(AFE_SOF_DL3, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL3")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(AFE_SOF_UL4, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL4")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(AFE_SOF_UL5, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL5")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static const struct sof_conn_stream g_sof_conn_streams[] = { + { "ETDM2_OUT_BE", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK}, + { "ETDM1_OUT_BE", "AFE_SOF_DL3", SOF_DMA_DL3, SNDRV_PCM_STREAM_PLAYBACK}, + { "UL_SRC1_BE", "AFE_SOF_UL4", SOF_DMA_UL4, SNDRV_PCM_STREAM_CAPTURE}, + { "ETDM2_IN_BE", "AFE_SOF_UL5", SOF_DMA_UL5, SNDRV_PCM_STREAM_CAPTURE}, +}; + +/* fixup the BE DAI link to match any values from topology */ +static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai_link *sof_dai_link = NULL; + struct snd_soc_pcm_runtime *runtime; + struct snd_soc_dai *cpu_dai; + int i, j, ret = 0; + + for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { + const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; + + if (strcmp(rtd->dai_link->name, conn->normal_link)) + continue; + + for_each_card_rtds(card, runtime) { + if (strcmp(runtime->dai_link->name, conn->sof_link)) + continue; + + for_each_rtd_cpu_dais(runtime, j, cpu_dai) { + if (cpu_dai->stream_active[conn->stream_dir] > 0) { + sof_dai_link = runtime->dai_link; + break; + } + } + break; + } + + if (sof_dai_link && sof_dai_link->be_hw_params_fixup) + ret = sof_dai_link->be_hw_params_fixup(runtime, params); + + break; + } + + if (!strcmp(rtd->dai_link->name, "ETDM2_IN_BE") || + !strcmp(rtd->dai_link->name, "ETDM1_OUT_BE")) { + mt8195_etdm_hw_params_fixup(runtime, params); + } + + return ret; +} + +static int mt8195_mt6359_rt1019_rt5682_card_late_probe(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *runtime; + struct snd_soc_component *sof_comp = NULL; + int i; + + /* 1. find sof component */ + for_each_card_rtds(card, runtime) { + for (i = 0; i < runtime->num_components; i++) { + if (!runtime->components[i]->driver->name) + continue; + if (!strcmp(runtime->components[i]->driver->name, "sof-audio-component")) { + sof_comp = runtime->components[i]; + break; + } + } + } + + if (!sof_comp) { + dev_info(card->dev, " probe without component\n"); + return 0; + } + /* 2. add route path and fixup callback */ + for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { + const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; + struct snd_soc_pcm_runtime *sof_rtd = NULL; + struct snd_soc_pcm_runtime *normal_rtd = NULL; + struct snd_soc_pcm_runtime *rtd = NULL; + + for_each_card_rtds(card, rtd) { + if (!strcmp(rtd->dai_link->name, conn->sof_link)) { + sof_rtd = rtd; + continue; + } + if (!strcmp(rtd->dai_link->name, conn->normal_link)) { + normal_rtd = rtd; + continue; + } + if (normal_rtd && sof_rtd) + break; + } + if (normal_rtd && sof_rtd) { + int j; + struct snd_soc_dai *cpu_dai; + + for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) { + struct snd_soc_dapm_route route; + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dapm_widget *play_widget = + cpu_dai->playback_widget; + struct snd_soc_dapm_widget *cap_widget = + cpu_dai->capture_widget; + memset(&route, 0, sizeof(route)); + if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && + cap_widget) { + snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) { + route.source = conn->sof_dma; + route.sink = p->sink->name; + snd_soc_dapm_add_routes(&card->dapm, &route, 1); + } + } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && + play_widget){ + snd_soc_dapm_widget_for_each_source_path(play_widget, p) { + route.source = p->source->name; + route.sink = conn->sof_dma; + snd_soc_dapm_add_routes(&card->dapm, &route, 1); + } + } else { + dev_err(cpu_dai->dev, "stream dir and widget not pair\n"); + } + } + normal_rtd->dai_link->be_hw_params_fixup = mt8195_dai_link_fixup; + } + } + + return 0; +} + static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { /* FE */ [DAI_LINK_DL2_FE] = { @@ -895,7 +1123,6 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { /* BE */ [DAI_LINK_DL_SRC_BE] = { .name = "DL_SRC_BE", - .init = mt8195_mt6359_init, .no_pcm = 1, .dpcm_playback = 1, SND_SOC_DAILINK_REG(DL_SRC_BE), @@ -964,6 +1191,7 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, .dpcm_capture = 1, SND_SOC_DAILINK_REG(PCM1_BE), }, @@ -979,6 +1207,31 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { .dpcm_capture = 1, SND_SOC_DAILINK_REG(UL_SRC2_BE), }, + /* SOF BE */ + [DAI_LINK_SOF_DL2_BE] = { + .name = "AFE_SOF_DL2", + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(AFE_SOF_DL2), + }, + [DAI_LINK_SOF_DL3_BE] = { + .name = "AFE_SOF_DL3", + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(AFE_SOF_DL3), + }, + [DAI_LINK_SOF_UL4_BE] = { + .name = "AFE_SOF_UL4", + .no_pcm = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(AFE_SOF_UL4), + }, + [DAI_LINK_SOF_UL5_BE] = { + .name = "AFE_SOF_UL5", + .no_pcm = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(AFE_SOF_UL5), + }, }; static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = { @@ -992,89 +1245,166 @@ static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = { .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_widgets), .dapm_routes = mt8195_mt6359_rt1019_rt5682_routes, .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_routes), + .set_bias_level_post = mt8195_set_bias_level_post, }; +static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node *np, + const char *propname) +{ + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + const char *dai_name = NULL; + int i, j, ret, num_links; + + num_links = of_property_count_strings(np, "mediatek,dai-link"); + + if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links)) { + dev_dbg(dev, "number of dai-link is invalid\n"); + return -EINVAL; + } + + card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; + + card->num_links = 0; + link = card->dai_link; + + for (i = 0; i < num_links; i++) { + ret = of_property_read_string_index(np, propname, i, &dai_name); + if (ret) { + dev_dbg(dev, "ASoC: Property '%s' index %d could not be read: %d\n", + propname, i, ret); + return -EINVAL; + } + + for (j = 0; j < ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links); j++) { + if (!strcmp(dai_name, mt8195_mt6359_rt1019_rt5682_dai_links[j].name)) { + memcpy(link, &mt8195_mt6359_rt1019_rt5682_dai_links[j], + sizeof(struct snd_soc_dai_link)); + link++; + card->num_links++; + break; + } + } + } + + if (card->num_links != num_links) + return -EINVAL; + + return 0; +} + static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card; struct snd_soc_dai_link *dai_link; struct mt8195_mt6359_rt1019_rt5682_priv *priv; + struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node; + int is5682s = 0; + int init6359 = 0; + int sof_on = 0; int ret, i; card->dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) { + dev_err(&pdev->dev, "%s new card name parsing error %d\n", + __func__, ret); + return ret; + } + + if (strstr(card->name, "_5682s")) + is5682s = 1; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->platform_node = of_parse_phandle(pdev->dev.of_node, - "mediatek,platform", 0); - if (!priv->platform_node) { + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n"); return -EINVAL; } + adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0); + if (adsp_node) + sof_on = 1; + + dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0); + hdmi_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,hdmi-codec", 0); + + if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) { + ret = mt8195_dailink_parse_of(card, pdev->dev.of_node, + "mediatek,dai-link"); + if (ret) { + dev_dbg(&pdev->dev, "Parse dai-link fail\n"); + return -EINVAL; + } + } else { + if (!sof_on) + card->num_links = DAI_LINK_REGULAR_NUM; + } + for_each_card_prelinks(card, i, dai_link) { - if (!dai_link->platforms->name) - dai_link->platforms->of_node = priv->platform_node; + if (!dai_link->platforms->name) { + if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on) + dai_link->platforms->of_node = adsp_node; + else + dai_link->platforms->of_node = platform_node; + } if (strcmp(dai_link->name, "DPTX_BE") == 0) { - priv->dp_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,dptx-codec", 0); - - if (!priv->dp_node) { + if (!dp_node) { dev_dbg(&pdev->dev, "No property 'dptx-codec'\n"); } else { - dai_link->codecs->of_node = priv->dp_node; + dai_link->codecs->of_node = dp_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_dptx_codec_init; } - } - - if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { - priv->hdmi_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,hdmi-codec", 0); - if (!priv->hdmi_node) { + } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { + if (!hdmi_node) { dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n"); } else { - dai_link->codecs->of_node = priv->hdmi_node; + dai_link->codecs->of_node = hdmi_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_hdmi_codec_init; } + } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 || + strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + dai_link->codecs->name = + is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME; + dai_link->codecs->dai_name = + is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; + } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 || + strcmp(dai_link->name, "UL_SRC1_BE") == 0 || + strcmp(dai_link->name, "UL_SRC2_BE") == 0) { + if (!init6359) { + dai_link->init = mt8195_mt6359_init; + init6359 = 1; + } } } + if (sof_on) + card->late_probe = mt8195_mt6359_rt1019_rt5682_card_late_probe; + snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); - of_node_put(priv->hdmi_node); - of_node_put(priv->dp_node); - of_node_put(priv->platform_node); - } + of_node_put(platform_node); + of_node_put(adsp_node); + of_node_put(dp_node); + of_node_put(hdmi_node); return ret; } -static int mt8195_mt6359_rt1019_rt5682_dev_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct mt8195_mt6359_rt1019_rt5682_priv *priv = - snd_soc_card_get_drvdata(card); - - of_node_put(priv->hdmi_node); - of_node_put(priv->dp_node); - of_node_put(priv->platform_node); - - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id mt8195_mt6359_rt1019_rt5682_dt_match[] = { {.compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",}, @@ -1096,7 +1426,6 @@ static struct platform_driver mt8195_mt6359_rt1019_rt5682_driver = { .pm = &mt8195_mt6359_rt1019_rt5682_pm_ops, }, .probe = mt8195_mt6359_rt1019_rt5682_dev_probe, - .remove = mt8195_mt6359_rt1019_rt5682_dev_remove, }; module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver); @@ -1104,5 +1433,6 @@ module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver); /* Module information */ MODULE_DESCRIPTION("MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver"); MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>"); -MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("YC Hung <yc.hung@mediatek.com>"); +MODULE_LICENSE("GPL"); MODULE_ALIAS("mt8195_mt6359_rt1019_rt5682 soc card"); diff --git a/sound/soc/mediatek/mt8195/mt8195-reg.h b/sound/soc/mediatek/mt8195/mt8195-reg.h index d06f9cf85a4e..d3871353db41 100644 --- a/sound/soc/mediatek/mt8195/mt8195-reg.h +++ b/sound/soc/mediatek/mt8195/mt8195-reg.h @@ -2550,6 +2550,7 @@ #define PCM_INTF_CON1_PCM_FMT(x) (((x) & 0x3) << 1) #define PCM_INTF_CON1_PCM_FMT_MASK (0x3 << 1) #define PCM_INTF_CON1_PCM_EN BIT(0) +#define PCM_INTF_CON1_PCM_EN_SHIFT 0 /* PCM_INTF_CON2 */ #define PCM_INTF_CON2_CLK_DOMAIN_SEL(x) (((x) & 0x3) << 23) diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index ba15d5762b0b..d299a70db7e5 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -218,34 +218,23 @@ static int aiu_clk_get(struct device *dev) int ret; aiu->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(aiu->pclk)) { - if (PTR_ERR(aiu->pclk) != -EPROBE_DEFER) - dev_err(dev, "Can't get the aiu pclk\n"); - return PTR_ERR(aiu->pclk); - } + if (IS_ERR(aiu->pclk)) + return dev_err_probe(dev, PTR_ERR(aiu->pclk), "Can't get the aiu pclk\n"); aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk"); - if (IS_ERR(aiu->spdif_mclk)) { - if (PTR_ERR(aiu->spdif_mclk) != -EPROBE_DEFER) - dev_err(dev, "Can't get the aiu spdif master clock\n"); - return PTR_ERR(aiu->spdif_mclk); - } + if (IS_ERR(aiu->spdif_mclk)) + return dev_err_probe(dev, PTR_ERR(aiu->spdif_mclk), + "Can't get the aiu spdif master clock\n"); ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids), &aiu->i2s); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Can't get the i2s clocks\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Can't get the i2s clocks\n"); ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids), &aiu->spdif); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Can't get the spdif clocks\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Can't get the spdif clocks\n"); ret = clk_prepare_enable(aiu->pclk); if (ret) { @@ -281,11 +270,8 @@ static int aiu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, aiu); ret = device_reset(dev); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to reset device\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to reset device\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index b9af2d513e09..bccfb770b339 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -351,20 +351,12 @@ int axg_fifo_probe(struct platform_device *pdev) } fifo->pclk = devm_clk_get(dev, NULL); - if (IS_ERR(fifo->pclk)) { - if (PTR_ERR(fifo->pclk) != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %ld\n", - PTR_ERR(fifo->pclk)); - return PTR_ERR(fifo->pclk); - } + if (IS_ERR(fifo->pclk)) + return dev_err_probe(dev, PTR_ERR(fifo->pclk), "failed to get pclk\n"); fifo->arb = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(fifo->arb)) { - if (PTR_ERR(fifo->arb) != -EPROBE_DEFER) - dev_err(dev, "failed to get arb reset: %ld\n", - PTR_ERR(fifo->arb)); - return PTR_ERR(fifo->arb); - } + if (IS_ERR(fifo->arb)) + return dev_err_probe(dev, PTR_ERR(fifo->arb), "failed to get arb reset\n"); fifo->irq = of_irq_get(dev->of_node, 0); if (fifo->irq <= 0) { diff --git a/sound/soc/meson/axg-pdm.c b/sound/soc/meson/axg-pdm.c index bfd37d49a73e..672e43a9729d 100644 --- a/sound/soc/meson/axg-pdm.c +++ b/sound/soc/meson/axg-pdm.c @@ -586,7 +586,6 @@ static int axg_pdm_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct axg_pdm *priv; void __iomem *regs; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -611,28 +610,16 @@ static int axg_pdm_probe(struct platform_device *pdev) } priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n"); priv->dclk = devm_clk_get(dev, "dclk"); - if (IS_ERR(priv->dclk)) { - ret = PTR_ERR(priv->dclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get dclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->dclk)) + return dev_err_probe(dev, PTR_ERR(priv->dclk), "failed to get dclk\n"); priv->sysclk = devm_clk_get(dev, "sysclk"); - if (IS_ERR(priv->sysclk)) { - ret = PTR_ERR(priv->sysclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get dclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->sysclk)) + return dev_err_probe(dev, PTR_ERR(priv->sysclk), "failed to get dclk\n"); return devm_snd_soc_register_component(dev, &axg_pdm_component_drv, &axg_pdm_dai_drv, 1); diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c index d0d09f945b48..4ba44e0d65d9 100644 --- a/sound/soc/meson/axg-spdifin.c +++ b/sound/soc/meson/axg-spdifin.c @@ -454,7 +454,6 @@ static int axg_spdifin_probe(struct platform_device *pdev) struct axg_spdifin *priv; struct snd_soc_dai_driver *dai_drv; void __iomem *regs; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -479,20 +478,12 @@ static int axg_spdifin_probe(struct platform_device *pdev) } priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n"); priv->refclk = devm_clk_get(dev, "refclk"); - if (IS_ERR(priv->refclk)) { - ret = PTR_ERR(priv->refclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get mclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->refclk)) + return dev_err_probe(dev, PTR_ERR(priv->refclk), "failed to get mclk\n"); dai_drv = axg_spdifin_get_dai_drv(dev, priv); if (IS_ERR(dai_drv)) { diff --git a/sound/soc/meson/axg-spdifout.c b/sound/soc/meson/axg-spdifout.c index e769a5ee6e27..3960d082e143 100644 --- a/sound/soc/meson/axg-spdifout.c +++ b/sound/soc/meson/axg-spdifout.c @@ -403,7 +403,6 @@ static int axg_spdifout_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct axg_spdifout *priv; void __iomem *regs; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -422,20 +421,12 @@ static int axg_spdifout_probe(struct platform_device *pdev) } priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n"); priv->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(priv->mclk)) { - ret = PTR_ERR(priv->mclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get mclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->mclk)) + return dev_err_probe(dev, PTR_ERR(priv->mclk), "failed to get mclk\n"); return devm_snd_soc_register_component(dev, &axg_spdifout_component_drv, axg_spdifout_dai_drv, ARRAY_SIZE(axg_spdifout_dai_drv)); diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c index cab7fa2851aa..9883dc777f63 100644 --- a/sound/soc/meson/axg-tdm-formatter.c +++ b/sound/soc/meson/axg-tdm-formatter.c @@ -255,7 +255,6 @@ int axg_tdm_formatter_probe(struct platform_device *pdev) const struct axg_tdm_formatter_driver *drv; struct axg_tdm_formatter *formatter; void __iomem *regs; - int ret; drv = of_device_get_match_data(dev); if (!drv) { @@ -282,57 +281,34 @@ int axg_tdm_formatter_probe(struct platform_device *pdev) /* Peripharal clock */ formatter->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(formatter->pclk)) { - ret = PTR_ERR(formatter->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->pclk)) + return dev_err_probe(dev, PTR_ERR(formatter->pclk), "failed to get pclk\n"); /* Formatter bit clock */ formatter->sclk = devm_clk_get(dev, "sclk"); - if (IS_ERR(formatter->sclk)) { - ret = PTR_ERR(formatter->sclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get sclk: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->sclk)) + return dev_err_probe(dev, PTR_ERR(formatter->sclk), "failed to get sclk\n"); /* Formatter sample clock */ formatter->lrclk = devm_clk_get(dev, "lrclk"); - if (IS_ERR(formatter->lrclk)) { - ret = PTR_ERR(formatter->lrclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get lrclk: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->lrclk)) + return dev_err_probe(dev, PTR_ERR(formatter->lrclk), "failed to get lrclk\n"); /* Formatter bit clock input multiplexer */ formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); - if (IS_ERR(formatter->sclk_sel)) { - ret = PTR_ERR(formatter->sclk_sel); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get sclk_sel: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->sclk_sel)) + return dev_err_probe(dev, PTR_ERR(formatter->sclk_sel), "failed to get sclk_sel\n"); /* Formatter sample clock input multiplexer */ formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); - if (IS_ERR(formatter->lrclk_sel)) { - ret = PTR_ERR(formatter->lrclk_sel); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get lrclk_sel: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->lrclk_sel)) + return dev_err_probe(dev, PTR_ERR(formatter->lrclk_sel), + "failed to get lrclk_sel\n"); /* Formatter dedicated reset line */ formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL); - if (IS_ERR(formatter->reset)) { - ret = PTR_ERR(formatter->reset); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get reset: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->reset)) + return dev_err_probe(dev, PTR_ERR(formatter->reset), "failed to get reset\n"); return devm_snd_soc_register_component(dev, drv->component_drv, NULL, 0); diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index db077773af7a..0c31934a9630 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -533,21 +533,13 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) /* Bit clock provided on the pad */ iface->sclk = devm_clk_get(dev, "sclk"); - if (IS_ERR(iface->sclk)) { - ret = PTR_ERR(iface->sclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get sclk: %d\n", ret); - return ret; - } + if (IS_ERR(iface->sclk)) + return dev_err_probe(dev, PTR_ERR(iface->sclk), "failed to get sclk\n"); /* Sample clock provided on the pad */ iface->lrclk = devm_clk_get(dev, "lrclk"); - if (IS_ERR(iface->lrclk)) { - ret = PTR_ERR(iface->lrclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get lrclk: %d\n", ret); - return ret; - } + if (IS_ERR(iface->lrclk)) + return dev_err_probe(dev, PTR_ERR(iface->lrclk), "failed to get lrclk\n"); /* * mclk maybe be missing when the cpu dai is in slave mode and @@ -558,13 +550,10 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) iface->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(iface->mclk)) { ret = PTR_ERR(iface->mclk); - if (ret == -ENOENT) { + if (ret == -ENOENT) iface->mclk = NULL; - } else { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get mclk: %d\n", ret); - return ret; - } + else + return dev_err_probe(dev, ret, "failed to get mclk\n"); } return devm_snd_soc_register_component(dev, diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index 29b0174f4b5c..2d8d5717fd8b 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -85,11 +85,9 @@ int meson_card_parse_dai(struct snd_soc_card *card, ret = of_parse_phandle_with_args(node, "sound-dai", "#sound-dai-cells", 0, &args); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "can't parse dai %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(card->dev, ret, "can't parse dai\n"); + *dai_of_node = args.np; return snd_soc_get_dai_name(&args, dai_name); diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c index 4c1349dd1e06..a9b8c4e77d40 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -258,18 +258,12 @@ static int t9015_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - if (PTR_ERR(priv->pclk) != -EPROBE_DEFER) - dev_err(dev, "failed to get core clock\n"); - return PTR_ERR(priv->pclk); - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get core clock\n"); priv->avdd = devm_regulator_get(dev, "AVDD"); - if (IS_ERR(priv->avdd)) { - if (PTR_ERR(priv->avdd) != -EPROBE_DEFER) - dev_err(dev, "failed to AVDD\n"); - return PTR_ERR(priv->avdd); - } + if (IS_ERR(priv->avdd)) + return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); ret = clk_prepare_enable(priv->pclk); if (ret) { diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index a6407f4388de..2412dc7e65d4 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -160,12 +160,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return 0; } diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index b2173847dc47..cf3e151bb635 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -169,6 +169,7 @@ config SND_SOC_SC7180 select SND_SOC_LPASS_SC7180 select SND_SOC_MAX98357A select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_ADAU7002 help To add support for audio on Qualcomm Technologies Inc. diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index ba2a98268ee4..f9d69375320e 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -17,6 +17,9 @@ #include <uapi/linux/input-event-codes.h> #include <dt-bindings/sound/apq8016-lpass.h> #include "common.h" +#include "qdsp6/q6afe.h" + +#define MI2S_COUNT (MI2S_QUATERNARY + 1) struct apq8016_sbc_data { struct snd_soc_card card; @@ -24,6 +27,7 @@ struct apq8016_sbc_data { void __iomem *spkr_iomux; struct snd_soc_jack jack; bool jack_setup; + int mi2s_clk_count[MI2S_COUNT]; }; #define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21) @@ -38,10 +42,10 @@ struct apq8016_sbc_data { #define SPKR_CTL_TLMM_WS_EN_SEL_MASK GENMASK(19, 18) #define SPKR_CTL_TLMM_WS_EN_SEL_SEC BIT(18) #define DEFAULT_MCLK_RATE 9600000 +#define MI2S_BCLK_RATE 1536000 -static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) +static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s) { - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai; struct snd_soc_component *component; struct snd_soc_card *card = rtd->card; @@ -49,7 +53,7 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) int i, rval; u32 value; - switch (cpu_dai->id) { + switch (mi2s) { case MI2S_PRIMARY: writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11, pdata->spkr_iomux); @@ -128,6 +132,13 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + return apq8016_dai_init(rtd, cpu_dai->id); +} + static void apq8016_sbc_add_ops(struct snd_soc_card *card) { struct snd_soc_dai_link *link; @@ -137,6 +148,113 @@ static void apq8016_sbc_add_ops(struct snd_soc_card *card) link->init = apq8016_sbc_dai_init; } +static int qdsp6_dai_get_lpass_id(struct snd_soc_dai *cpu_dai) +{ + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: + case PRIMARY_MI2S_TX: + return MI2S_PRIMARY; + case SECONDARY_MI2S_RX: + case SECONDARY_MI2S_TX: + return MI2S_SECONDARY; + case TERTIARY_MI2S_RX: + case TERTIARY_MI2S_TX: + return MI2S_TERTIARY; + case QUATERNARY_MI2S_RX: + case QUATERNARY_MI2S_TX: + return MI2S_QUATERNARY; + default: + return -EINVAL; + } +} + +static int msm8916_qdsp6_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + return apq8016_dai_init(rtd, qdsp6_dai_get_lpass_id(cpu_dai)); +} + +static int msm8916_qdsp6_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int mi2s, ret; + + mi2s = qdsp6_dai_get_lpass_id(cpu_dai); + if (mi2s < 0) + return mi2s; + + if (++data->mi2s_clk_count[mi2s] > 1) + return 0; + + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, MI2S_BCLK_RATE, 0); + if (ret) + dev_err(card->dev, "Failed to enable LPAIF bit clk: %d\n", ret); + return ret; +} + +static void msm8916_qdsp6_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int mi2s, ret; + + mi2s = qdsp6_dai_get_lpass_id(cpu_dai); + if (mi2s < 0) + return; + + if (--data->mi2s_clk_count[mi2s] > 0) + return; + + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, 0, 0); + if (ret) + dev_err(card->dev, "Failed to disable LPAIF bit clk: %d\n", ret); +} + +static const struct snd_soc_ops msm8916_qdsp6_be_ops = { + .startup = msm8916_qdsp6_startup, + .shutdown = msm8916_qdsp6_shutdown, +}; + +static int msm8916_qdsp6_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static void msm8916_qdsp6_add_ops(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link; + int i; + + /* Make it obvious to userspace that QDSP6 is used */ + card->components = "qdsp6"; + + for_each_card_prelinks(card, i, link) { + if (link->no_pcm) { + link->init = msm8916_qdsp6_dai_init; + link->ops = &msm8916_qdsp6_be_ops; + link->be_hw_params_fixup = msm8916_qdsp6_be_hw_params_fixup; + } + } +} + static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { SND_SOC_DAPM_MIC("Handset Mic", NULL), @@ -148,11 +266,16 @@ static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { static int apq8016_sbc_platform_probe(struct platform_device *pdev) { + void (*add_ops)(struct snd_soc_card *card); struct device *dev = &pdev->dev; struct snd_soc_card *card; struct apq8016_sbc_data *data; int ret; + add_ops = device_get_match_data(&pdev->dev); + if (!add_ops) + return -EINVAL; + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -177,12 +300,13 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, data); - apq8016_sbc_add_ops(card); + add_ops(card); return devm_snd_soc_register_card(&pdev->dev, card); } static const struct of_device_id apq8016_sbc_device_id[] __maybe_unused = { - { .compatible = "qcom,apq8016-sbc-sndcard" }, + { .compatible = "qcom,apq8016-sbc-sndcard", .data = apq8016_sbc_add_ops }, + { .compatible = "qcom,msm8916-qdsp6-sndcard", .data = msm8916_qdsp6_add_ops }, {}, }; MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id); diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 2e1c618f7529..c407684ce1a2 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -26,6 +26,12 @@ int qcom_snd_parse_of(struct snd_soc_card *card) return ret; } + if (of_property_read_bool(dev->of_node, "widgets")) { + ret = snd_soc_of_parse_audio_simple_widgets(card, "widgets"); + if (ret) + return ret; + } + /* DAPM routes */ if (of_property_read_bool(dev->of_node, "audio-routing")) { ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); @@ -39,6 +45,10 @@ int qcom_snd_parse_of(struct snd_soc_card *card) return ret; } + ret = snd_soc_of_parse_pin_switches(card, "pin-switches"); + if (ret) + return ret; + ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); if (ret) return ret; @@ -94,9 +104,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "%s: error getting cpu dai name: %d\n", - link->name, ret); + dev_err_probe(card->dev, ret, + "%s: error getting cpu dai name\n", link->name); goto err; } @@ -116,9 +125,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card) if (codec) { ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "%s: codec dai not found: %d\n", - link->name, ret); + dev_err_probe(card->dev, ret, + "%s: codec dai not found\n", link->name); goto err; } diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 13598ef5bacb..f424d7aa389a 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -615,7 +615,7 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, graph = kzalloc(sizeof(*graph), GFP_KERNEL); if (!graph) { ret = -ENOMEM; - goto err; + goto put_ar_graph; } graph->apm = apm; @@ -630,14 +630,16 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, init_waitqueue_head(&graph->cmd_wait); graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph); - if (!graph->port) { - kfree(graph); - ret = -ENOMEM; - goto err; + if (IS_ERR(graph->port)) { + ret = PTR_ERR(graph->port); + goto free_graph; } return graph; -err: + +free_graph: + kfree(graph); +put_ar_graph: kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph); return ERR_PTR(ret); } diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c index 768566bb57a5..37225ef2563a 100644 --- a/sound/soc/qcom/sc7180.c +++ b/sound/soc/qcom/sc7180.c @@ -17,6 +17,7 @@ #include <uapi/linux/input-event-codes.h> #include "../codecs/rt5682.h" +#include "../codecs/rt5682s.h" #include "common.h" #include "lpass.h" @@ -128,7 +129,21 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream) struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - int ret; + int pll_id, pll_source, pll_in, pll_out, clk_id, ret; + + if (!strcmp(codec_dai->name, "rt5682-aif1")) { + pll_source = RT5682_PLL1_S_MCLK; + pll_id = 0; + clk_id = RT5682_SCLK_S_PLL1; + pll_out = RT5682_PLL1_FREQ; + pll_in = DEFAULT_MCLK_RATE; + } else if (!strcmp(codec_dai->name, "rt5682s-aif1")) { + pll_source = RT5682S_PLL_S_MCLK; + pll_id = RT5682S_PLL2; + clk_id = RT5682S_SCLK_S_PLL2; + pll_out = RT5682_PLL1_FREQ; + pll_in = DEFAULT_MCLK_RATE; + } switch (cpu_dai->id) { case MI2S_PRIMARY: @@ -145,16 +160,15 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream) SND_SOC_DAIFMT_I2S); /* Configure PLL1 for codec */ - ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK, - DEFAULT_MCLK_RATE, RT5682_PLL1_FREQ); + ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, + pll_in, pll_out); if (ret) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; } /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, - RT5682_PLL1_FREQ, + ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out, SND_SOC_CLOCK_IN); if (ret) dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 0adfc5708949..5c1d13eccbee 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -33,6 +33,7 @@ struct sdm845_snd_data { struct snd_soc_jack jack; bool jack_setup; + bool slim_port_setup; bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; uint32_t pri_mi2s_clk_count; @@ -56,8 +57,8 @@ static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream, int ret = 0, i; for_each_rtd_codec_dais(rtd, i, codec_dai) { - sruntime = snd_soc_dai_get_sdw_stream(codec_dai, - substream->stream); + sruntime = snd_soc_dai_get_stream(codec_dai, + substream->stream); if (sruntime != ERR_PTR(-ENOTSUPP)) pdata->sruntime[cpu_dai->id] = sruntime; @@ -224,6 +225,7 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = rtd->dai_link; struct snd_jack *jack; /* * Codec SLIMBUS configuration @@ -276,6 +278,10 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) } break; case SLIMBUS_0_RX...SLIMBUS_6_TX: + /* setting up wcd multiple times for slim port is redundant */ + if (pdata->slim_port_setup || !link->no_pcm) + return 0; + for_each_rtd_codec_dais(rtd, i, codec_dai) { rval = snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), @@ -295,8 +301,10 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) dev_warn(card->dev, "Failed to set jack: %d\n", rval); return rval; } - } + + pdata->slim_port_setup = true; + break; default: break; diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index b2ca2579810b..114a29e01c0f 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -136,8 +136,8 @@ static int sm8250_snd_hw_params(struct snd_pcm_substream *substream, case TX_CODEC_DMA_TX_2: case TX_CODEC_DMA_TX_3: for_each_rtd_codec_dais(rtd, i, codec_dai) { - sruntime = snd_soc_dai_get_sdw_stream(codec_dai, - substream->stream); + sruntime = snd_soc_dai_get_stream(codec_dai, + substream->stream); if (sruntime != ERR_PTR(-ENOTSUPP)) pdata->sruntime[cpu_dai->id] = sruntime; } diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c index 33a00774746d..b052642ea620 100644 --- a/sound/soc/rockchip/rk3288_hdmi_analog.c +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -249,13 +249,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, machine); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret == -EPROBE_DEFER) - return -EPROBE_DEFER; - if (ret) { - dev_err(&pdev->dev, - "Soc register card failed %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Soc register card failed\n"); return ret; } diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 313ab650f8d9..5265e546b124 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -585,19 +585,16 @@ static int aries_audio_probe(struct platform_device *pdev) extcon_np = of_parse_phandle(np, "extcon", 0); priv->usb_extcon = extcon_find_edev_by_node(extcon_np); - if (IS_ERR(priv->usb_extcon)) { - if (PTR_ERR(priv->usb_extcon) != -EPROBE_DEFER) - dev_err(dev, "Failed to get extcon device"); - return PTR_ERR(priv->usb_extcon); - } + if (IS_ERR(priv->usb_extcon)) + return dev_err_probe(dev, PTR_ERR(priv->usb_extcon), + "Failed to get extcon device"); of_node_put(extcon_np); priv->adc = devm_iio_channel_get(dev, "headset-detect"); - if (IS_ERR(priv->adc)) { - if (PTR_ERR(priv->adc) != -EPROBE_DEFER) - dev_err(dev, "Failed to get ADC channel"); - return PTR_ERR(priv->adc); - } + if (IS_ERR(priv->adc)) + return dev_err_probe(dev, PTR_ERR(priv->adc), + "Failed to get ADC channel"); + if (priv->adc->channel->type != IIO_VOLTAGE) return -EINVAL; diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c index 606ac5e33a8e..a5dc640d0d76 100644 --- a/sound/soc/samsung/arndale.c +++ b/sound/soc/samsung/arndale.c @@ -174,9 +174,8 @@ static int arndale_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(card->dev, card); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card() failed: %d\n", ret); + dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card() failed\n"); goto err_put_of_nodes; } return 0; diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index 66bcc2f97544..c3f1b054e238 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -360,6 +360,8 @@ static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream) buf->addr = idma.lp_tx_addr; buf->bytes = idma_hardware.buffer_bytes_max; buf->area = (unsigned char * __force)ioremap(buf->addr, buf->bytes); + if (!buf->area) + return -ENOMEM; return 0; } diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 390f2dd735ad..34067cc314ff 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -325,9 +325,8 @@ static int littlemill_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 998d10cf8c94..7b12ccd2a9b2 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -183,9 +183,8 @@ static int lowland_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index ca643a488c3c..4ff12e2e704f 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -311,9 +311,7 @@ static int odroid_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "snd_soc_register_card() failed: %d\n", - ret); + dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); goto err_put_clk_i2s; } diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index 7661b637946d..821ad1eb1b79 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -179,8 +179,8 @@ static int smdk_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c index 029448f5bedb..d77dc54cae9c 100644 --- a/sound/soc/samsung/smdk_wm8994pcm.c +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -118,8 +118,8 @@ static int snd_smdk_probe(struct platform_device *pdev) smdk_pcm.dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, &smdk_pcm); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return ret; } diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index 6da674e901ca..02372109c251 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -212,12 +212,9 @@ static int snow_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(dev, card); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card failed\n"); return ret; } diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index f5f6ba00d073..37b1f4f60b21 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -330,9 +330,8 @@ static int speyside_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index a2c77e6defec..d611ec9e5325 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -612,8 +612,7 @@ static int tm2_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to register card: %d\n", ret); + dev_err_probe(dev, ret, "Failed to register card\n"); goto dai_node_put; } diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index 15223d860cb7..8d3149a47a4c 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -229,9 +229,8 @@ static int tobermory_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index fa0cc08f70ec..e8d98b362f9d 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -1020,7 +1020,12 @@ static int rz_ssi_probe(struct platform_device *pdev) reset_control_deassert(ssi->rstc); pm_runtime_enable(&pdev->dev); - pm_runtime_resume_and_get(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) { + pm_runtime_disable(ssi->dev); + reset_control_assert(ssi->rstc); + return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n"); + } spin_lock_init(&ssi->lock); dev_set_drvdata(&pdev->dev, ssi); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index c76ff9c59dfb..c0664f94990c 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -932,6 +932,34 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream) return 0; } +void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, + snd_pcm_sframes_t *codec_delay) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_component *component; + snd_pcm_sframes_t delay; + int i; + + /* + * We're looking for the delay through the full audio path so it needs to + * be the maximum of the Components doing transmit and the maximum of the + * Components doing receive (ie, all CPUs and all CODECs) rather than + * just the maximum of all Components. + */ + for_each_rtd_components(rtd, i, component) { + if (!component->driver->delay) + continue; + + delay = component->driver->delay(component, substream); + + if (snd_soc_component_is_codec(component)) + *codec_delay = max(*codec_delay, delay); + else + *cpu_delay = max(*cpu_delay, delay); + } +} + int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index dcf6be4c4aaa..434e61b46983 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2315,7 +2315,6 @@ int snd_soc_register_card(struct snd_soc_card *card) mutex_init(&card->mutex); mutex_init(&card->dapm_mutex); mutex_init(&card->pcm_mutex); - spin_lock_init(&card->dpcm_lock); return snd_soc_bind_card(card); } @@ -2824,6 +2823,56 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); +int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop) +{ + const unsigned int nb_controls_max = 16; + const char **strings, *control_name; + struct snd_kcontrol_new *controls; + struct device *dev = card->dev; + unsigned int i, nb_controls; + int ret; + + if (!of_property_read_bool(dev->of_node, prop)) + return 0; + + strings = devm_kcalloc(dev, nb_controls_max, + sizeof(*strings), GFP_KERNEL); + if (!strings) + return -ENOMEM; + + ret = of_property_read_string_array(dev->of_node, prop, + strings, nb_controls_max); + if (ret < 0) + return ret; + + nb_controls = (unsigned int)ret; + + controls = devm_kcalloc(dev, nb_controls, + sizeof(*controls), GFP_KERNEL); + if (!controls) + return -ENOMEM; + + for (i = 0; i < nb_controls; i++) { + control_name = devm_kasprintf(dev, GFP_KERNEL, + "%s Switch", strings[i]); + if (!control_name) + return -ENOMEM; + + controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + controls[i].name = control_name; + controls[i].info = snd_soc_dapm_info_pin_switch; + controls[i].get = snd_soc_dapm_get_pin_switch; + controls[i].put = snd_soc_dapm_put_pin_switch; + controls[i].private_value = (unsigned long)strings[i]; + } + + card->controls = controls; + card->num_controls = nb_controls; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_pin_switches); + int snd_soc_of_get_slot_mask(struct device_node *np, const char *prop_name, unsigned int *mask) diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 3db0fcf24385..6078afe335f8 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -453,18 +453,6 @@ void snd_soc_dai_shutdown(struct snd_soc_dai *dai, soc_dai_mark_pop(dai, substream, startup); } -snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream) -{ - int delay = 0; - - if (dai->driver->ops && - dai->driver->ops->delay) - delay = dai->driver->ops->delay(substream, dai); - - return delay; -} - int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd, int num) { @@ -693,6 +681,34 @@ int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream, return 0; } +void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, + snd_pcm_sframes_t *codec_delay) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *dai; + int i; + + /* + * We're looking for the delay through the full audio path so it needs to + * be the maximum of the DAIs doing transmit and the maximum of the DAIs + * doing receive (ie, all CPUs and all CODECs) rather than just the maximum + * of all DAIs. + */ + + /* for CPU */ + for_each_rtd_cpu_dais(rtd, i, dai) + if (dai->driver->ops && + dai->driver->ops->delay) + *cpu_delay = max(*cpu_delay, dai->driver->ops->delay(substream, dai)); + + /* for Codec */ + for_each_rtd_codec_dais(rtd, i, dai) + if (dai->driver->ops && + dai->driver->ops->delay) + *codec_delay = max(*codec_delay, dai->driver->ops->delay(substream, dai)); +} + int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, struct snd_compr_stream *cstream) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 4d41ad302802..7abfc48b26ca 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -27,6 +27,37 @@ #include <sound/soc-link.h> #include <sound/initval.h> +static inline void snd_soc_dpcm_mutex_lock(struct snd_soc_pcm_runtime *rtd) +{ + mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); +} + +static inline void snd_soc_dpcm_mutex_unlock(struct snd_soc_pcm_runtime *rtd) +{ + mutex_unlock(&rtd->card->pcm_mutex); +} + +#define snd_soc_dpcm_mutex_assert_held(rtd) \ + lockdep_assert_held(&(rtd)->card->pcm_mutex) + +static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd, + int stream) +{ + snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream)); +} + +#define snd_soc_dpcm_stream_lock_irqsave(rtd, stream, flags) \ + snd_pcm_stream_lock_irqsave(snd_soc_dpcm_get_substream(rtd, stream), flags) + +static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd, + int stream) +{ + snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream)); +} + +#define snd_soc_dpcm_stream_unlock_irqrestore(rtd, stream, flags) \ + snd_pcm_stream_unlock_irqrestore(snd_soc_dpcm_get_substream(rtd, stream), flags) + #define DPCM_MAX_BE_USERS 8 static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd) @@ -73,7 +104,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params; struct snd_soc_dpcm *dpcm; ssize_t offset = 0; - unsigned long flags; /* FE state */ offset += scnprintf(buf + offset, size - offset, @@ -101,7 +131,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, goto out; } - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; params = &dpcm->hw_params; @@ -122,7 +151,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, params_channels(params), params_rate(params)); } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); out: return offset; } @@ -145,11 +173,13 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; + snd_soc_dpcm_mutex_lock(fe); for_each_pcm_streams(stream) if (snd_soc_dai_stream_valid(asoc_rtd_to_cpu(fe, 0), stream)) offset += dpcm_show_state(fe, stream, buf + offset, out_count - offset); + snd_soc_dpcm_mutex_unlock(fe); ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); @@ -221,14 +251,14 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe, struct snd_pcm_substream *substream = snd_soc_dpcm_get_substream(fe, stream); - snd_pcm_stream_lock_irq(substream); + snd_soc_dpcm_stream_lock_irq(fe, stream); if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) { dpcm_fe_dai_do_trigger(substream, fe->dpcm[stream].trigger_pending - 1); fe->dpcm[stream].trigger_pending = 0; } fe->dpcm[stream].runtime_update = state; - snd_pcm_stream_unlock_irq(substream); + snd_soc_dpcm_stream_unlock_irq(fe, stream); } static void dpcm_set_be_update_state(struct snd_soc_pcm_runtime *be, @@ -256,7 +286,7 @@ void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai; int i; - lockdep_assert_held(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_assert_held(rtd); for_each_rtd_dais(rtd, i, dai) snd_soc_dai_action(dai, stream, action); @@ -309,6 +339,8 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, { struct snd_soc_dpcm *dpcm; + snd_soc_dpcm_mutex_assert_held(fe); + for_each_dpcm_be(fe, dir, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; @@ -646,14 +678,14 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream, return ret; } -static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) +static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, int rollback) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; struct snd_soc_dai *dai; int i; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); if (!rollback) snd_soc_runtime_deactivate(rtd, substream->stream); @@ -665,9 +697,6 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) soc_pcm_components_close(substream, rollback); - - mutex_unlock(&rtd->card->pcm_mutex); - snd_soc_pcm_component_pm_runtime_put(rtd, substream, rollback); for_each_rtd_components(rtd, i, component) @@ -682,9 +711,21 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) * freed here. The cpu DAI, codec DAI, machine and components are also * shutdown. */ +static int __soc_pcm_close(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + return soc_pcm_clean(rtd, substream, 0); +} + +/* PCM close ops for non-DPCM streams */ static int soc_pcm_close(struct snd_pcm_substream *substream) { - return soc_pcm_clean(substream, 0); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + + snd_soc_dpcm_mutex_lock(rtd); + soc_pcm_clean(rtd, substream, 0); + snd_soc_dpcm_mutex_unlock(rtd); + return 0; } static int soc_hw_sanity_check(struct snd_pcm_substream *substream) @@ -730,21 +771,21 @@ config_err: * then initialized and any private data can be allocated. This also calls * startup for the cpu DAI, component, machine and codec DAI. */ -static int soc_pcm_open(struct snd_pcm_substream *substream) +static int __soc_pcm_open(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; struct snd_soc_dai *dai; int i, ret = 0; + snd_soc_dpcm_mutex_assert_held(rtd); + for_each_rtd_components(rtd, i, component) pinctrl_pm_select_default_state(component->dev); ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream); if (ret < 0) - goto pm_err; - - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + goto err; ret = soc_pcm_components_open(substream); if (ret < 0) @@ -791,16 +832,26 @@ dynamic: snd_soc_runtime_activate(rtd, substream->stream); ret = 0; err: - mutex_unlock(&rtd->card->pcm_mutex); -pm_err: if (ret < 0) { - soc_pcm_clean(substream, 1); + soc_pcm_clean(rtd, substream, 1); dev_err(rtd->dev, "%s() failed (%d)", __func__, ret); } return ret; } +/* PCM open ops for non-DPCM streams */ +static int soc_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_open(rtd, substream); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; +} + static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) { /* @@ -816,13 +867,13 @@ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) * rate, etc. This function is non atomic and can be called multiple times, * it can refer to the runtime info. */ -static int soc_pcm_prepare(struct snd_pcm_substream *substream) +static int __soc_pcm_prepare(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *dai; int i, ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); ret = snd_soc_link_prepare(substream); if (ret < 0) @@ -850,14 +901,24 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dai_digital_mute(dai, 0, substream->stream); out: - mutex_unlock(&rtd->card->pcm_mutex); - if (ret < 0) dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret); return ret; } +/* PCM prepare ops for non-DPCM streams */ +static int soc_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_prepare(rtd, substream); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; +} + static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params, unsigned int mask) { @@ -869,13 +930,13 @@ static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params, interval->max = channels; } -static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback) +static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, int rollback) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *dai; int i; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); /* clear the corresponding DAIs parameters when going to be inactive */ for_each_rtd_dais(rtd, i, dai) { @@ -900,16 +961,28 @@ static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback) if (snd_soc_dai_stream_valid(dai, substream->stream)) snd_soc_dai_hw_free(dai, substream, rollback); - mutex_unlock(&rtd->card->pcm_mutex); return 0; } /* * Frees resources allocated by hw_params, can be called multiple times */ +static int __soc_pcm_hw_free(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + return soc_pcm_hw_clean(rtd, substream, 0); +} + +/* hw_free PCM ops for non-DPCM streams */ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { - return soc_pcm_hw_clean(substream, 0); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_hw_free(rtd, substream); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; } /* @@ -917,15 +990,15 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) * function can also be called multiple times and can allocate buffers * (using snd_pcm_lib_* ). It's non-atomic. */ -static int soc_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); ret = soc_pcm_params_symmetry(substream, params); if (ret) @@ -997,16 +1070,27 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_pcm_component_hw_params(substream, params); out: - mutex_unlock(&rtd->card->pcm_mutex); - if (ret < 0) { - soc_pcm_hw_clean(substream, 1); + soc_pcm_hw_clean(rtd, substream, 1); dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret); } return ret; } +/* hw_params PCM ops for non-DPCM streams */ +static int soc_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_hw_params(rtd, substream, params); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; +} + static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); @@ -1080,41 +1164,22 @@ start_err: /* * soc level wrapper for pointer callback * If cpu_dai, codec_dai, component driver has the delay callback, then - * the runtime->delay will be updated accordingly. + * the runtime->delay will be updated via snd_soc_pcm_component/dai_delay(). */ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai; - struct snd_soc_dai *codec_dai; struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t offset = 0; - snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t codec_delay = 0; snd_pcm_sframes_t cpu_delay = 0; - int i; - - /* clearing the previous total delay */ - runtime->delay = 0; offset = snd_soc_pcm_component_pointer(substream); - /* base delay if assigned in pointer callback */ - delay = runtime->delay; + /* should be called *after* snd_soc_pcm_component_pointer() */ + snd_soc_pcm_dai_delay(substream, &cpu_delay, &codec_delay); + snd_soc_pcm_component_delay(substream, &cpu_delay, &codec_delay); - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - cpu_delay = max(cpu_delay, - snd_soc_dai_delay(cpu_dai, substream)); - } - delay += cpu_delay; - - for_each_rtd_codec_dais(rtd, i, codec_dai) { - codec_delay = max(codec_delay, - snd_soc_dai_delay(codec_dai, substream)); - } - delay += codec_delay; - - runtime->delay = delay; + runtime->delay = cpu_delay + codec_delay; return offset; } @@ -1123,8 +1188,11 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream) { + struct snd_pcm_substream *fe_substream; + struct snd_pcm_substream *be_substream; struct snd_soc_dpcm *dpcm; - unsigned long flags; + + snd_soc_dpcm_mutex_assert_held(fe); /* only add new dpcms */ for_each_dpcm_be(fe, stream, dpcm) { @@ -1132,7 +1200,21 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, return 0; } - dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL); + fe_substream = snd_soc_dpcm_get_substream(fe, stream); + be_substream = snd_soc_dpcm_get_substream(be, stream); + + if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) { + dev_err(be->dev, "%s: FE is atomic but BE is nonatomic, invalid configuration\n", + __func__); + return -EINVAL; + } + if (fe_substream->pcm->nonatomic && !be_substream->pcm->nonatomic) { + dev_warn(be->dev, "%s: FE is nonatomic but BE is not, forcing BE as nonatomic\n", + __func__); + be_substream->pcm->nonatomic = 1; + } + + dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_ATOMIC); if (!dpcm) return -ENOMEM; @@ -1140,10 +1222,10 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, dpcm->fe = fe; be->dpcm[stream].runtime = fe->dpcm[stream].runtime; dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; - spin_lock_irqsave(&fe->card->dpcm_lock, flags); + snd_soc_dpcm_stream_lock_irq(fe, stream); list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); + snd_soc_dpcm_stream_unlock_irq(fe, stream); dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n", stream ? "capture" : "playback", fe->dai_link->name, @@ -1186,8 +1268,10 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm, *d; - unsigned long flags; + snd_soc_dpcm_mutex_assert_held(fe); + + snd_soc_dpcm_stream_lock_irq(fe, stream); for_each_dpcm_be_safe(fe, stream, dpcm, d) { dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n", stream ? "capture" : "playback", @@ -1205,12 +1289,11 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) dpcm_remove_debugfs_state(dpcm); - spin_lock_irqsave(&fe->card->dpcm_lock, flags); list_del(&dpcm->list_be); list_del(&dpcm->list_fe); - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); kfree(dpcm); } + snd_soc_dpcm_stream_unlock_irq(fe, stream); } /* get BE for DAI widget and stream */ @@ -1434,12 +1517,9 @@ int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; - unsigned long flags; - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_be(fe, stream, dpcm) dpcm_set_be_update_state(dpcm->be, stream, SND_SOC_DPCM_UPDATE_NO); - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); } void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, @@ -1475,12 +1555,12 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, continue; if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) { - soc_pcm_hw_free(be_substream); + __soc_pcm_hw_free(be, be_substream); be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; } } - soc_pcm_close(be_substream); + __soc_pcm_close(be, be_substream); be_substream->runtime = NULL; be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; } @@ -1528,7 +1608,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) stream ? "capture" : "playback", be->dai_link->name); be_substream->runtime = be->dpcm[stream].runtime; - err = soc_pcm_open(be_substream); + err = __soc_pcm_open(be, be_substream); if (err < 0) { be->dpcm[stream].users--; if (be->dpcm[stream].users < 0) @@ -1539,7 +1619,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; goto unwind; } - + be->dpcm[stream].be_start = 0; be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; count++; } @@ -1772,7 +1852,7 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name); /* start the DAI frontend */ - ret = soc_pcm_open(fe_substream); + ret = __soc_pcm_open(fe, fe_substream); if (ret < 0) goto unwind; @@ -1803,6 +1883,8 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int stream = substream->stream; + snd_soc_dpcm_mutex_assert_held(fe); + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); /* shutdown the BEs */ @@ -1811,7 +1893,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name); /* now shutdown the frontend */ - soc_pcm_close(substream); + __soc_pcm_close(fe, substream); /* run the stream stop event */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); @@ -1856,7 +1938,7 @@ void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: hw_free BE %s\n", be->dai_link->name); - soc_pcm_hw_free(be_substream); + __soc_pcm_hw_free(be, be_substream); be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; } @@ -1867,13 +1949,13 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int stream = substream->stream; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); /* call hw_free on the frontend */ - soc_pcm_hw_free(substream); + soc_pcm_hw_clean(fe, substream, 0); /* only hw_params backends that are either sinks or sources * to this frontend DAI */ @@ -1882,7 +1964,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); return 0; } @@ -1926,7 +2008,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: hw_params BE %s\n", be->dai_link->name); - ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); + ret = __soc_pcm_hw_params(be, be_substream, &dpcm->hw_params); if (ret < 0) goto unwind; @@ -1956,7 +2038,7 @@ unwind: (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) continue; - soc_pcm_hw_free(be_substream); + __soc_pcm_hw_free(be, be_substream); } return ret; @@ -1968,7 +2050,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int ret, stream = substream->stream; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); memcpy(&fe->dpcm[stream].hw_params, params, @@ -1982,7 +2064,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, params_channels(params), params_format(params)); /* call hw_params on the frontend */ - ret = soc_pcm_hw_params(substream, params); + ret = __soc_pcm_hw_params(fe, substream, params); if (ret < 0) dpcm_be_dai_hw_free(fe, stream); else @@ -1990,7 +2072,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, out: dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); if (ret < 0) dev_err(fe->dev, "ASoC: %s failed (%d)\n", __func__, ret); @@ -2003,6 +2085,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, { struct snd_soc_pcm_runtime *be; struct snd_soc_dpcm *dpcm; + unsigned long flags; int ret = 0; for_each_dpcm_be(fe, stream, dpcm) { @@ -2011,89 +2094,128 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, be = dpcm->be; be_substream = snd_soc_dpcm_get_substream(be, stream); + snd_soc_dpcm_stream_lock_irqsave(be, stream, flags); + /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) - continue; + goto next; dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n", be->dai_link->name, cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && + if (!be->dpcm[stream].be_start && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) - continue; + goto next; + + be->dpcm[stream].be_start++; + if (be->dpcm[stream].be_start != 1) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start--; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_RESUME: if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) - continue; + goto next; + + be->dpcm[stream].be_start++; + if (be->dpcm[stream].be_start != 1) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start--; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) - continue; + if (!be->dpcm[stream].be_start && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) + goto next; + + be->dpcm[stream].be_start++; + if (be->dpcm[stream].be_start != 1) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start--; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_STOP: if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) - continue; + goto next; - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) - continue; + if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START) + be->dpcm[stream].be_start--; + + if (be->dpcm[stream].be_start != 0) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START) + be->dpcm[stream].be_start++; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; break; case SNDRV_PCM_TRIGGER_SUSPEND: if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) - continue; + goto next; - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) - continue; + be->dpcm[stream].be_start--; + if (be->dpcm[stream].be_start != 0) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start++; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) - continue; + goto next; - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) - continue; + be->dpcm[stream].be_start--; + if (be->dpcm[stream].be_start != 0) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start++; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; break; } +next: + snd_soc_dpcm_stream_unlock_irqrestore(be, stream, flags); + if (ret) + break; } -end: if (ret < 0) dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n", __func__, be->dai_link->name, ret); @@ -2261,7 +2383,7 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: prepare BE %s\n", be->dai_link->name); - ret = soc_pcm_prepare(be_substream); + ret = __soc_pcm_prepare(be, be_substream); if (ret < 0) break; @@ -2279,7 +2401,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int stream = substream->stream, ret = 0; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); @@ -2298,7 +2420,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) goto out; /* call prepare on the frontend */ - ret = soc_pcm_prepare(substream); + ret = __soc_pcm_prepare(fe, substream); if (ret < 0) goto out; @@ -2306,7 +2428,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) out: dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); if (ret < 0) dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret); @@ -2357,7 +2479,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) struct snd_soc_dpcm *dpcm; enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; int ret = 0; - unsigned long flags; dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n", stream ? "capture" : "playback", fe->dai_link->name); @@ -2426,7 +2547,6 @@ close: dpcm_be_dai_shutdown(fe, stream); disconnect: /* disconnect any pending BEs */ - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; @@ -2438,7 +2558,6 @@ disconnect: be->dpcm[stream].state == SND_SOC_DPCM_STATE_NEW) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); if (ret < 0) dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret); @@ -2513,7 +2632,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) struct snd_soc_pcm_runtime *fe; int ret = 0; - mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass); /* shutdown all old paths first */ for_each_card_rtds(card, fe) { ret = soc_dpcm_fe_runtime_update(fe, 0); @@ -2529,7 +2648,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) } out: - mutex_unlock(&card->mutex); + mutex_unlock(&card->pcm_mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update); @@ -2540,6 +2659,8 @@ static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream) struct snd_soc_dpcm *dpcm; int stream = fe_substream->stream; + snd_soc_dpcm_mutex_assert_held(fe); + /* mark FE's links ready to prune */ for_each_dpcm_be(fe, stream, dpcm) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; @@ -2554,12 +2675,12 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); int ret; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); ret = dpcm_fe_dai_shutdown(fe_substream); dpcm_fe_dai_cleanup(fe_substream); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); return ret; } @@ -2570,7 +2691,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) int ret; int stream = fe_substream->stream; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); fe->dpcm[stream].runtime = fe_substream->runtime; ret = dpcm_path_get(fe, stream, &list); @@ -2587,7 +2708,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) dpcm_clear_pending_state(fe, stream); dpcm_path_put(&list); open_end: - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); return ret; } @@ -2848,10 +2969,8 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, struct snd_soc_dpcm *dpcm; int state; int ret = 1; - unsigned long flags; int i; - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_fe(be, stream, dpcm) { if (dpcm->fe == fe) @@ -2865,7 +2984,6 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, } } } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); /* it's safe to do this BE DAI */ return ret; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index f5b9e66ac3b8..2630df024dff 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -56,7 +56,7 @@ struct soc_tplg { const struct firmware *fw; /* runtime FW parsing */ - const u8 *pos; /* read postion */ + const u8 *pos; /* read position */ const u8 *hdr_pos; /* header position */ unsigned int pass; /* pass number */ diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 041c54639c4d..1a7d6cefd3b7 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -40,12 +40,14 @@ config SND_SOC_SOF_ACPI_DEV config SND_SOC_SOF_OF tristate "SOF OF enumeration support" depends on OF || COMPILE_TEST - select SND_SOC_SOF help This adds support for Device Tree enumeration. This option is - required to enable i.MX8 devices. + required to enable i.MX8 or Mediatek devices. Say Y if you need this option. If unsure select "N". +config SND_SOC_SOF_OF_DEV + tristate + config SND_SOC_SOF_COMPRESS bool select SND_SOC_COMPRESS @@ -61,7 +63,7 @@ config SND_SOC_SOF_DEBUG_PROBES config SND_SOC_SOF_DEVELOPER_SUPPORT bool "SOF developer options support" - depends on EXPERT + depends on EXPERT && SND_SOC_SOF help This option unlocks SOF developer options for debug/performance/ code hardening. @@ -192,6 +194,14 @@ config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST Say Y if you want to enable IPC flood test. If unsure, select "N". +config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR + bool "SOF enable IPC message injector" + help + This option enables the IPC message injector which can be used to send + crafted IPC messages to the DSP to test its robustness. + Say Y if you want to enable the IPC message injector. + If unsure, select "N". + config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT bool "SOF retain DSP context on any FW exceptions" help @@ -223,8 +233,10 @@ config SND_SOC_SOF_PROBE_WORK_QUEUE When selected, the probe is handled in two steps, for example to avoid lockdeps if request_module is used in the probe. +source "sound/soc/sof/amd/Kconfig" source "sound/soc/sof/imx/Kconfig" source "sound/soc/sof/intel/Kconfig" +source "sound/soc/sof/mediatek/Kconfig" source "sound/soc/sof/xtensa/Kconfig" endif diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 06e5f49f7ee8..964b429146be 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -17,9 +17,11 @@ obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o -obj-$(CONFIG_SND_SOC_SOF_OF) += snd-sof-of.o +obj-$(CONFIG_SND_SOC_SOF_OF_DEV) += snd-sof-of.o obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ +obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/ obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/ +obj-$(CONFIG_SND_SOC_SOF_MTK_TOPLEVEL) += mediatek/ diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig new file mode 100644 index 000000000000..085232e04582 --- /dev/null +++ b/sound/soc/sof/amd/Kconfig @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +# This file is provided under a dual BSD/GPLv2 license. When using or +# redistributing this file, you may do so under either license. +# +# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + +config SND_SOC_SOF_AMD_TOPLEVEL + tristate "SOF support for AMD audio DSPs" + depends on X86 || COMPILE_TEST + help + This adds support for Sound Open Firmware for AMD platforms. + Say Y if you have such a device. + If unsure select "N". + +if SND_SOC_SOF_AMD_TOPLEVEL + +config SND_SOC_SOF_AMD_COMMON + tristate + select SND_SOC_SOF + select SND_SOC_SOF_PCI_DEV + select SND_AMD_ACP_CONFIG + select SND_SOC_ACPI if ACPI + help + This option is not user-selectable but automatically handled by + 'select' statements at a higher level + +config SND_SOC_SOF_AMD_RENOIR + tristate "SOF support for RENOIR" + depends on SND_SOC_SOF_PCI + select SND_SOC_SOF_AMD_COMMON + help + Select this option for SOF support on AMD Renoir platform +endif diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile new file mode 100644 index 000000000000..7b9f1a0af3c8 --- /dev/null +++ b/sound/soc/sof/amd/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +# This file is provided under a dual BSD/GPLv2 license. When using or +# redistributing this file, you may do so under either license. +# +# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + +snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o +snd-sof-amd-renoir-objs := pci-rn.o renoir.o + +obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o +obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) +=snd-sof-amd-renoir.o diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h new file mode 100644 index 000000000000..63f13c111b24 --- /dev/null +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * + * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + */ + +#ifndef _ACP_DSP_IP_OFFSET_H +#define _ACP_DSP_IP_OFFSET_H + +/* Registers from ACP_DMA_0 block */ +#define ACP_DMA_CNTL_0 0x00 +#define ACP_DMA_DSCR_STRT_IDX_0 0x20 +#define ACP_DMA_DSCR_CNT_0 0x40 +#define ACP_DMA_PRIO_0 0x60 +#define ACP_DMA_CUR_DSCR_0 0x80 +#define ACP_DMA_ERR_STS_0 0xC0 +#define ACP_DMA_DESC_BASE_ADDR 0xE0 +#define ACP_DMA_DESC_MAX_NUM_DSCR 0xE4 +#define ACP_DMA_CH_STS 0xE8 +#define ACP_DMA_CH_GROUP 0xEC +#define ACP_DMA_CH_RST_STS 0xF0 + +/* Registers from ACP_DSP_0 block */ +#define ACP_DSP0_RUNSTALL 0x414 + +/* Registers from ACP_AXI2AXIATU block */ +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0xC00 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0xC04 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0xC08 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0xC0C +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0xC10 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0xC14 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0xC18 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0xC1C +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0xC28 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0xC2C +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0xC30 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0xC34 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0xC38 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0xC3C +#define ACPAXI2AXI_ATU_CTRL 0xC40 +#define ACP_SOFT_RESET 0x1000 + +#define ACP_I2S_PIN_CONFIG 0x1400 + +/* Registers from ACP_PGFSM block */ +#define ACP_PGFSM_CONTROL 0x141C +#define ACP_PGFSM_STATUS 0x1420 + +/* Registers from ACP_INTR block */ +#define ACP_EXTERNAL_INTR_ENB 0x1800 +#define ACP_EXTERNAL_INTR_CNTL 0x1804 +#define ACP_EXTERNAL_INTR_STAT 0x1808 +#define ACP_DSP_SW_INTR_CNTL 0x1814 +#define ACP_DSP_SW_INTR_STAT 0x1818 +#define ACP_SW_INTR_TRIG 0x181C +#define ACP_ERROR_STATUS 0x18C4 + +/* Registers from ACP_SHA block */ +#define ACP_SHA_DSP_FW_QUALIFIER 0x1C70 +#define ACP_SHA_DMA_CMD 0x1CB0 +#define ACP_SHA_MSG_LENGTH 0x1CB4 +#define ACP_SHA_DMA_STRT_ADDR 0x1CB8 +#define ACP_SHA_DMA_DESTINATION_ADDR 0x1CBC +#define ACP_SHA_DMA_CMD_STS 0x1CC0 +#define ACP_SHA_DMA_ERR_STATUS 0x1CC4 +#define ACP_SHA_TRANSFER_BYTE_CNT 0x1CC8 +#define ACP_SHA_PSP_ACK 0x1C74 + +#define ACP_SCRATCH_REG_0 0x10000 + +#endif diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c new file mode 100644 index 000000000000..e132223b4c66 --- /dev/null +++ b/sound/soc/sof/amd/acp-ipc.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Balakishore Pati <Balakishore.pati@amd.com> +// Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + +/* ACP-specific SOF IPC code */ + +#include <linux/module.h> +#include "../ops.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes) +{ + memcpy_to_scratch(sdev, offset, message, bytes); +} +EXPORT_SYMBOL_NS(acp_mailbox_write, SND_SOC_SOF_AMD_COMMON); + +void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes) +{ + memcpy_from_scratch(sdev, offset, message, bytes); +} +EXPORT_SYMBOL_NS(acp_mailbox_read, SND_SOC_SOF_AMD_COMMON); + +static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev = adata->dev; + u32 swintr_trigger; + + swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG); + swintr_trigger |= 0x01; + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG, swintr_trigger); +} + +static void acp_ipc_host_msg_set(struct snd_sof_dev *sdev) +{ + unsigned int host_msg = offsetof(struct scratch_ipc_conf, sof_host_msg_write); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg, 1); +} + +static void acp_dsp_ipc_host_done(struct snd_sof_dev *sdev) +{ + unsigned int dsp_msg = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg, 0); +} + +static void acp_dsp_ipc_dsp_done(struct snd_sof_dev *sdev) +{ + unsigned int dsp_ack = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack, 0); +} + +int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box); + + acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size); + acp_ipc_host_msg_set(sdev); + + /* Trigger host to dsp interrupt for the msg */ + acpbus_trigger_host_to_dsp_swintr(adata); + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, SND_SOC_SOF_AMD_COMMON); + +static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc_reply reply; + struct sof_ipc_cmd_hdr *hdr; + unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box); + int ret = 0; + + /* + * Sometimes, there is unexpected reply ipc arriving. The reply + * ipc belongs to none of the ipcs sent from driver. + * In this case, the driver must ignore the ipc. + */ + if (!msg) { + dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); + return; + } + hdr = msg->msg_data; + if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) || + hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) { + /* + * memory windows are powered off before sending IPC reply, + * so we can't read the mailbox for CTX_SAVE and PM_GATE + * replies. + */ + reply.error = 0; + reply.hdr.cmd = SOF_IPC_GLB_REPLY; + reply.hdr.size = sizeof(reply); + memcpy(msg->reply_data, &reply, sizeof(reply)); + goto out; + } + /* get IPC reply from DSP in the mailbox */ + acp_mailbox_read(sdev, offset, &reply, sizeof(reply)); + if (reply.error < 0) { + memcpy(msg->reply_data, &reply, sizeof(reply)); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size && + !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) { + dev_err(sdev->dev, "reply expected %zu got %u bytes\n", + msg->reply_size, reply.hdr.size); + ret = -EINVAL; + } + /* read the message */ + if (msg->reply_size > 0) + acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size); + } +out: + msg->reply_error = ret; +} + +irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = context; + unsigned int dsp_msg_write = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write); + unsigned int dsp_ack_write = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write); + bool ipc_irq = false; + int dsp_msg, dsp_ack; + + dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write); + if (dsp_msg) { + snd_sof_ipc_msgs_rx(sdev); + acp_dsp_ipc_host_done(sdev); + ipc_irq = true; + } + + dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write); + if (dsp_ack) { + spin_lock_irq(&sdev->ipc_lock); + /* handle immediate reply from DSP core */ + acp_dsp_ipc_get_reply(sdev); + snd_sof_ipc_reply(sdev, 0); + /* set the done bit */ + acp_dsp_ipc_dsp_done(sdev); + spin_unlock_irq(&sdev->ipc_lock); + ipc_irq = true; + } + + if (!ipc_irq) + dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + void *p, size_t sz) +{ + unsigned int offset = offsetof(struct scratch_ipc_conf, sof_out_box); + + if (!substream || !sdev->stream_box.size) + acp_mailbox_read(sdev, offset, p, sz); + + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + const struct sof_ipc_pcm_params_reply *reply) +{ + /* TODO: Implement stream hw params to validate stream offset */ + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_pcm_params, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) +{ + return ACP_SCRATCH_MEMORY_ADDRESS; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, SND_SOC_SOF_AMD_COMMON); + +MODULE_DESCRIPTION("AMD ACP sof-ipc driver"); diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c new file mode 100644 index 000000000000..2dc15ae38155 --- /dev/null +++ b/sound/soc/sof/amd/acp-loader.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + +/* + * Hardware interface for ACP DSP Firmware binaries loader + */ + +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "../ops.h" +#include "acp-dsp-offset.h" +#include "acp.h" + +#define FW_BIN 0 +#define FW_DATA_BIN 1 + +#define FW_BIN_PTE_OFFSET 0x00 +#define FW_DATA_BIN_PTE_OFFSET 0x08 + +#define ACP_DSP_RUN 0x00 + +int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *dest, size_t size) +{ + switch (blk_type) { + case SOF_FW_BLK_TYPE_SRAM: + offset = offset - ACP_SCRATCH_MEMORY_ADDRESS; + memcpy_from_scratch(sdev, offset, dest, size); + break; + default: + dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_NS(acp_dsp_block_read, SND_SOC_SOF_AMD_COMMON); + +int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *src, size_t size) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct pci_dev *pci = to_pci_dev(sdev->dev); + struct acp_dev_data *adata; + void *dest; + u32 dma_size, page_count; + unsigned int size_fw; + + adata = sdev->pdata->hw_pdata; + + switch (blk_type) { + case SOF_FW_BLK_TYPE_IRAM: + if (!adata->bin_buf) { + size_fw = plat_data->fw->size; + page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; + dma_size = page_count * ACP_PAGE_SIZE; + adata->bin_buf = dma_alloc_coherent(&pci->dev, dma_size, + &adata->sha_dma_addr, + GFP_ATOMIC); + if (!adata->bin_buf) + return -ENOMEM; + } + adata->fw_bin_size = size + offset; + dest = adata->bin_buf + offset; + break; + case SOF_FW_BLK_TYPE_DRAM: + if (!adata->data_buf) { + adata->data_buf = dma_alloc_coherent(&pci->dev, + ACP_DEFAULT_DRAM_LENGTH, + &adata->dma_addr, + GFP_ATOMIC); + if (!adata->data_buf) + return -ENOMEM; + } + dest = adata->data_buf + offset; + adata->fw_data_bin_size = size + offset; + break; + case SOF_FW_BLK_TYPE_SRAM: + offset = offset - ACP_SCRATCH_MEMORY_ADDRESS; + memcpy_to_scratch(sdev, offset, src, size); + return 0; + default: + dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type); + return -EINVAL; + } + + memcpy(dest, src, size); + return 0; +} +EXPORT_SYMBOL_NS(acp_dsp_block_write, SND_SOC_SOF_AMD_COMMON); + +int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type) +{ + return type; +} +EXPORT_SYMBOL_NS(acp_get_bar_index, SND_SOC_SOF_AMD_COMMON); + +static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev; + unsigned int low, high; + dma_addr_t addr; + u16 page_idx; + u32 offset; + + sdev = adata->dev; + + switch (type) { + case FW_BIN: + offset = FW_BIN_PTE_OFFSET; + addr = adata->sha_dma_addr; + break; + case FW_DATA_BIN: + offset = adata->fw_bin_page_count * 8; + addr = adata->dma_addr; + break; + default: + dev_err(sdev->dev, "Invalid data type %x\n", type); + return; + } + + for (page_idx = 0; page_idx < num_pages; page_idx++) { + low = lower_32_bits(addr); + high = upper_32_bits(addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low); + high |= BIT(31); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high); + offset += 8; + addr += PAGE_SIZE; + } +} + +/* pre fw run operations */ +int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = to_pci_dev(sdev->dev); + struct snd_sof_pdata *plat_data = sdev->pdata; + struct acp_dev_data *adata; + unsigned int src_addr, size_fw; + u32 page_count, dma_size; + int ret; + + adata = sdev->pdata->hw_pdata; + size_fw = adata->fw_bin_size; + + page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; + adata->fw_bin_page_count = page_count; + + configure_pte_for_fw_loading(FW_BIN, page_count, adata); + ret = configure_and_run_sha_dma(adata, adata->bin_buf, ACP_SYSTEM_MEMORY_WINDOW, + ACP_IRAM_BASE_ADDRESS, size_fw); + if (ret < 0) { + dev_err(sdev->dev, "SHA DMA transfer failed status: %d\n", ret); + return ret; + } + configure_pte_for_fw_loading(FW_DATA_BIN, ACP_DRAM_PAGE_COUNT, adata); + + src_addr = ACP_SYSTEM_MEMORY_WINDOW + page_count * ACP_PAGE_SIZE; + ret = configure_and_run_dma(adata, src_addr, ACP_DATA_RAM_BASE_ADDRESS, + adata->fw_data_bin_size); + if (ret < 0) { + dev_err(sdev->dev, "acp dma configuration failed: %d\n", ret); + return ret; + } + + ret = acp_dma_status(adata, 0); + if (ret < 0) + dev_err(sdev->dev, "acp dma transfer status: %d\n", ret); + + /* Free memory once DMA is complete */ + dma_size = (PAGE_ALIGN(plat_data->fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE; + dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr); + dma_free_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, adata->data_buf, adata->dma_addr); + adata->bin_buf = NULL; + adata->data_buf = NULL; + + return ret; +} +EXPORT_SYMBOL_NS(acp_dsp_pre_fw_run, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_dsp_run(struct snd_sof_dev *sdev) +{ + int val; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL, ACP_DSP_RUN); + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL); + dev_dbg(sdev->dev, "ACP_DSP0_RUNSTALL : 0x%0x\n", val); + + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_dsp_run, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c new file mode 100644 index 000000000000..5b23830cb1f3 --- /dev/null +++ b/sound/soc/sof/amd/acp-pcm.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + +/* + * PCM interface for generic AMD audio ACP DSP block + */ +#include <sound/pcm_params.h> + +#include "../ops.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params) +{ + struct acp_dsp_stream *stream = substream->runtime->private_data; + unsigned int buf_offset, index; + u32 size; + int ret; + + size = ipc_params->buffer.size; + stream->num_pages = ipc_params->buffer.pages; + stream->dmab = substream->runtime->dma_buffer_p; + + ret = acp_dsp_stream_config(sdev, stream); + if (ret < 0) { + dev_err(sdev->dev, "stream configuration failed\n"); + return ret; + } + + ipc_params->buffer.phy_addr = stream->reg_offset; + ipc_params->stream_tag = stream->stream_tag; + + /* write buffer size of stream in scratch memory */ + + buf_offset = offsetof(struct scratch_reg_conf, buf_size); + index = stream->stream_tag - 1; + buf_offset = buf_offset + index * 4; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + buf_offset, size); + + return 0; +} +EXPORT_SYMBOL_NS(acp_pcm_hw_params, SND_SOC_SOF_AMD_COMMON); + +int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +{ + struct acp_dsp_stream *stream; + + stream = acp_dsp_stream_get(sdev, 0); + if (!stream) + return -ENODEV; + + substream->runtime->private_data = stream; + stream->substream = substream; + + return 0; +} +EXPORT_SYMBOL_NS(acp_pcm_open, SND_SOC_SOF_AMD_COMMON); + +int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +{ + struct acp_dsp_stream *stream; + + stream = substream->runtime->private_data; + if (!stream) { + dev_err(sdev->dev, "No open stream\n"); + return -EINVAL; + } + + stream->substream = NULL; + substream->runtime->private_data = NULL; + + return acp_dsp_stream_put(sdev, stream); +} +EXPORT_SYMBOL_NS(acp_pcm_close, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp-stream.c b/sound/soc/sof/amd/acp-stream.c new file mode 100644 index 000000000000..f2837bfbdb20 --- /dev/null +++ b/sound/soc/sof/amd/acp-stream.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + +/* + * Hardware interface for generic AMD audio DSP ACP IP + */ + +#include "../ops.h" +#include "acp-dsp-offset.h" +#include "acp.h" + +#define PTE_GRP1_OFFSET 0x00000000 +#define PTE_GRP2_OFFSET 0x00800000 +#define PTE_GRP3_OFFSET 0x01000000 +#define PTE_GRP4_OFFSET 0x01800000 +#define PTE_GRP5_OFFSET 0x02000000 +#define PTE_GRP6_OFFSET 0x02800000 +#define PTE_GRP7_OFFSET 0x03000000 +#define PTE_GRP8_OFFSET 0x03800000 + +int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream) +{ + unsigned int pte_reg, pte_size, phy_addr_offset, index; + int stream_tag = stream->stream_tag; + u32 low, high, offset, reg_val; + dma_addr_t addr; + int page_idx; + + switch (stream_tag) { + case 1: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_1; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1; + offset = offsetof(struct scratch_reg_conf, grp1_pte); + stream->reg_offset = PTE_GRP1_OFFSET; + break; + case 2: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_2; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2; + offset = offsetof(struct scratch_reg_conf, grp2_pte); + stream->reg_offset = PTE_GRP2_OFFSET; + break; + case 3: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_3; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3; + offset = offsetof(struct scratch_reg_conf, grp3_pte); + stream->reg_offset = PTE_GRP3_OFFSET; + break; + case 4: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_4; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4; + offset = offsetof(struct scratch_reg_conf, grp4_pte); + stream->reg_offset = PTE_GRP4_OFFSET; + break; + case 5: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5; + offset = offsetof(struct scratch_reg_conf, grp5_pte); + stream->reg_offset = PTE_GRP5_OFFSET; + break; + case 6: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_6; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6; + offset = offsetof(struct scratch_reg_conf, grp6_pte); + stream->reg_offset = PTE_GRP6_OFFSET; + break; + case 7: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_7; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7; + offset = offsetof(struct scratch_reg_conf, grp7_pte); + stream->reg_offset = PTE_GRP7_OFFSET; + break; + case 8: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_8; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8; + offset = offsetof(struct scratch_reg_conf, grp8_pte); + stream->reg_offset = PTE_GRP8_OFFSET; + break; + default: + dev_err(sdev->dev, "Invalid stream tag %d\n", stream_tag); + return -EINVAL; + } + + /* write phy_addr in scratch memory */ + + phy_addr_offset = offsetof(struct scratch_reg_conf, reg_offset); + index = stream_tag - 1; + phy_addr_offset = phy_addr_offset + index * 4; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + + phy_addr_offset, stream->reg_offset); + + /* Group Enable */ + reg_val = ACP_SRAM_PTE_OFFSET + offset; + snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_reg, reg_val | BIT(31)); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_size, PAGE_SIZE_4K_ENABLE); + + for (page_idx = 0; page_idx < stream->num_pages; page_idx++) { + addr = snd_sgbuf_get_addr(stream->dmab, page_idx * PAGE_SIZE); + + /* Load the low address of page int ACP SRAM through SRBM */ + low = lower_32_bits(addr); + high = upper_32_bits(addr); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low); + + high |= BIT(31); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high); + /* Move to next physically contiguous page */ + offset += 8; + } + + return 0; +} + +struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + struct acp_dsp_stream *stream = adata->stream_buf; + int i; + + for (i = 0; i < ACP_MAX_STREAM; i++, stream++) { + if (stream->active) + continue; + + /* return stream if tag not specified*/ + if (!tag) { + stream->active = 1; + return stream; + } + + /* check if this is the requested stream tag */ + if (stream->stream_tag == tag) { + stream->active = 1; + return stream; + } + } + + dev_err(sdev->dev, "stream %d active or no inactive stream\n", tag); + return NULL; +} +EXPORT_SYMBOL_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON); + +int acp_dsp_stream_put(struct snd_sof_dev *sdev, + struct acp_dsp_stream *acp_stream) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + struct acp_dsp_stream *stream = adata->stream_buf; + int i; + + /* Free an active stream */ + for (i = 0; i < ACP_MAX_STREAM; i++, stream++) { + if (stream == acp_stream) { + stream->active = 0; + return 0; + } + } + + dev_err(sdev->dev, "Cannot find active stream tag %d\n", acp_stream->stream_tag); + return -EINVAL; +} +EXPORT_SYMBOL_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON); + +int acp_dsp_stream_init(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + int i; + + for (i = 0; i < ACP_MAX_STREAM; i++) { + adata->stream_buf[i].sdev = sdev; + adata->stream_buf[i].active = 0; + adata->stream_buf[i].stream_tag = i + 1; + } + return 0; +} +EXPORT_SYMBOL_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp-trace.c b/sound/soc/sof/amd/acp-trace.c new file mode 100644 index 000000000000..fa4da8947186 --- /dev/null +++ b/sound/soc/sof/amd/acp-trace.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Vishnuvardhanrao Ravuapati <vishnuvardhanrao.ravulapati@amd.com> +// V Sujith Kumar Reddy <Vsujithkumar.Reddy@amd.com> + +/*This file support Host TRACE Logger driver callback for SOF FW */ + +#include "acp.h" + +#define ACP_LOGGER_STREAM 8 +#define NUM_PAGES 16 + +int acp_sof_trace_release(struct snd_sof_dev *sdev) +{ + struct acp_dsp_stream *stream; + struct acp_dev_data *adata; + int ret; + + adata = sdev->pdata->hw_pdata; + stream = adata->dtrace_stream; + ret = acp_dsp_stream_put(sdev, stream); + if (ret < 0) { + dev_err(sdev->dev, "Failed to release trace stream\n"); + return ret; + } + + adata->dtrace_stream = NULL; + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_trace_release, SND_SOC_SOF_AMD_COMMON); + +static int acp_sof_trace_prepare(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_params_ext *params) +{ + struct acp_dsp_stream *stream; + struct acp_dev_data *adata; + int ret; + + adata = sdev->pdata->hw_pdata; + stream = adata->dtrace_stream; + stream->dmab = &sdev->dmatb; + stream->num_pages = NUM_PAGES; + + ret = acp_dsp_stream_config(sdev, stream); + if (ret < 0) { + dev_err(sdev->dev, "Failed to configure trace stream\n"); + return ret; + } + + params->buffer.phy_addr = stream->reg_offset; + params->stream_tag = stream->stream_tag; + + return 0; +} + +int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) +{ + struct sof_ipc_dma_trace_params_ext *params; + struct acp_dsp_stream *stream; + struct acp_dev_data *adata; + int ret; + + adata = sdev->pdata->hw_pdata; + stream = acp_dsp_stream_get(sdev, ACP_LOGGER_STREAM); + if (!stream) + return -ENODEV; + + adata->dtrace_stream = stream; + params = container_of(stream_tag, struct sof_ipc_dma_trace_params_ext, stream_tag); + ret = acp_sof_trace_prepare(sdev, params); + if (ret < 0) { + acp_dsp_stream_put(sdev, stream); + return ret; + } + + *stream_tag = stream->stream_tag; + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_trace_init, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c new file mode 100644 index 000000000000..fe9b7dc5bc86 --- /dev/null +++ b/sound/soc/sof/amd/acp.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com> +// Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + +/* + * Hardware interface for generic AMD ACP processor + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "../ops.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +static int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) +{ + pci_write_config_dword(dev, 0x60, smn_addr); + pci_write_config_dword(dev, 0x64, data); + + return 0; +} + +static int smn_read(struct pci_dev *dev, u32 smn_addr, u32 *data) +{ + pci_write_config_dword(dev, 0x60, smn_addr); + pci_read_config_dword(dev, 0x64, data); + + return 0; +} + +static void configure_acp_groupregisters(struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev = adata->dev; + + /* Group Enable */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1, + ACP_SRAM_PTE_OFFSET | BIT(31)); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1, + PAGE_SIZE_4K_ENABLE); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID); +} + +static void init_dma_descriptor(struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int addr; + + addr = ACP_SRAM_PTE_OFFSET + offsetof(struct scratch_reg_conf, dma_desc); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_BASE_ADDR, addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_MAX_NUM_DSCR, ACP_MAX_DESC_CNT); +} + +static void configure_dma_descriptor(struct acp_dev_data *adata, unsigned short idx, + struct dma_descriptor *dscr_info) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int offset; + + offset = ACP_SCRATCH_REG_0 + offsetof(struct scratch_reg_conf, dma_desc) + + idx * sizeof(struct dma_descriptor); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset, dscr_info->src_addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x4, dscr_info->dest_addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x8, dscr_info->tx_cnt.u32_all); +} + +static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch, + unsigned int idx, unsigned int dscr_count) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int val, status; + int ret; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32), + ACP_DMA_CH_RST | ACP_DMA_CH_GRACEFUL_RST_EN); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_RST_STS, val, + val & (1 << ch), ACP_REG_POLL_INTERVAL, + ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) { + status = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_ERROR_STATUS); + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_ERR_STS_0 + ch * sizeof(u32)); + + dev_err(sdev->dev, "ACP_DMA_ERR_STS :0x%x ACP_ERROR_STATUS :0x%x\n", val, status); + return ret; + } + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, (ACP_DMA_CNTL_0 + ch * sizeof(u32)), 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_CNT_0 + ch * sizeof(u32), dscr_count); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_STRT_IDX_0 + ch * sizeof(u32), idx); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_PRIO_0 + ch * sizeof(u32), 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32), ACP_DMA_CH_RUN); + + return ret; +} + +static int acpbus_dma_start(struct acp_dev_data *adata, unsigned int ch, + unsigned int dscr_count, struct dma_descriptor *dscr_info) +{ + struct snd_sof_dev *sdev = adata->dev; + int ret; + u16 dscr; + + if (!dscr_info || !dscr_count) + return -EINVAL; + + for (dscr = 0; dscr < dscr_count; dscr++) + configure_dma_descriptor(adata, dscr, dscr_info++); + + ret = config_dma_channel(adata, ch, 0, dscr_count); + if (ret < 0) + dev_err(sdev->dev, "config dma ch failed:%d\n", ret); + + return ret; +} + +int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr, + unsigned int dest_addr, int dsp_data_size) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int desc_count, index; + int ret; + + for (desc_count = 0; desc_count < ACP_MAX_DESC && dsp_data_size >= 0; + desc_count++, dsp_data_size -= ACP_PAGE_SIZE) { + adata->dscr_info[desc_count].src_addr = src_addr + desc_count * ACP_PAGE_SIZE; + adata->dscr_info[desc_count].dest_addr = dest_addr + desc_count * ACP_PAGE_SIZE; + adata->dscr_info[desc_count].tx_cnt.bits.count = ACP_PAGE_SIZE; + if (dsp_data_size < ACP_PAGE_SIZE) + adata->dscr_info[desc_count].tx_cnt.bits.count = dsp_data_size; + } + + ret = acpbus_dma_start(adata, 0, desc_count, adata->dscr_info); + if (ret) + dev_err(sdev->dev, "acpbus_dma_start failed\n"); + + /* Clear descriptor array */ + for (index = 0; index < desc_count; index++) + memset(&adata->dscr_info[index], 0x00, sizeof(struct dma_descriptor)); + + return ret; +} + +static int psp_fw_validate(struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev = adata->dev; + int timeout; + u32 data; + + smn_write(adata->smn_dev, MP0_C2PMSG_26_REG, MBOX_ACP_SHA_DMA_COMMAND); + + for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) { + msleep(20); + smn_read(adata->smn_dev, MP0_C2PMSG_26_REG, &data); + if (data & MBOX_READY_MASK) + return 0; + } + + dev_err(sdev->dev, "FW validation timedout: status %x\n", data & MBOX_STATUS_MASK); + return -ETIMEDOUT; +} + +int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, + unsigned int start_addr, unsigned int dest_addr, + unsigned int image_length) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int tx_count, fw_qualifier, val; + int ret; + + if (!image_addr) { + dev_err(sdev->dev, "SHA DMA image address is NULL\n"); + return -EINVAL; + } + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD); + if (val & ACP_SHA_RUN) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RESET); + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD_STS, + val, val & ACP_SHA_RESET, + ACP_REG_POLL_INTERVAL, + ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, "SHA DMA Failed to Reset\n"); + return ret; + } + } + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_DESTINATION_ADDR, dest_addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_MSG_LENGTH, image_length); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RUN); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_TRANSFER_BYTE_CNT, + tx_count, tx_count == image_length, + ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, "SHA DMA Failed to Transfer Length %x\n", tx_count); + return ret; + } + + ret = psp_fw_validate(adata); + if (ret) + return ret; + + fw_qualifier = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER); + if (!(fw_qualifier & DSP_FW_RUN_ENABLE)) { + dev_err(sdev->dev, "PSP validation failed\n"); + return -EINVAL; + } + + return 0; +} + +int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int val; + int ret = 0; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32)); + if (val & ACP_DMA_CH_RUN) { + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_STS, val, !val, + ACP_REG_POLL_INTERVAL, + ACP_DMA_COMPLETE_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "DMA_CHANNEL %d status timeout\n", ch); + } + + return ret; +} + +void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes) +{ + unsigned int reg_offset = offset + ACP_SCRATCH_REG_0; + int i, j; + + for (i = 0, j = 0; i < bytes; i = i + 4, j++) + dst[j] = snd_sof_dsp_read(sdev, ACP_DSP_BAR, reg_offset + i); +} + +void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes) +{ + unsigned int reg_offset = offset + ACP_SCRATCH_REG_0; + int i, j; + + for (i = 0, j = 0; i < bytes; i = i + 4, j++) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, reg_offset + i, src[j]); +} + +static int acp_memory_init(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + + snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_CNTL, + ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK); + configure_acp_groupregisters(adata); + init_dma_descriptor(adata); + + return 0; +} + +static irqreturn_t acp_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = context; + unsigned int val; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT); + if (val & ACP_SHA_STAT) { + /* Clear SHA interrupt raised by PSP */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT, val); + return IRQ_HANDLED; + } + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT); + if (val & ACP_DSP_TO_HOST_IRQ) { + sof_ops(sdev)->irq_thread(irq, sdev); + val |= ACP_DSP_TO_HOST_IRQ; + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT, val); + return IRQ_HANDLED; + } + + return IRQ_NONE; +}; + +static irqreturn_t acp_irq_handler(int irq, void *dev_id) +{ + struct snd_sof_dev *sdev = dev_id; + unsigned int val; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT); + if (val) + return IRQ_WAKE_THREAD; + + return IRQ_NONE; +} + +static int acp_power_on(struct snd_sof_dev *sdev) +{ + unsigned int val; + int ret; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_PGFSM_STATUS); + + if (val == ACP_POWERED_ON) + return 0; + + if (val & ACP_PGFSM_STATUS_MASK) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_PGFSM_CONTROL, + ACP_PGFSM_CNTL_POWER_ON_MASK); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_PGFSM_STATUS, val, !val, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n"); + + return ret; +} + +static int acp_reset(struct snd_sof_dev *sdev) +{ + unsigned int val; + int ret; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_ASSERT_RESET); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, + val & ACP_SOFT_RESET_DONE_MASK, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, "timeout asserting reset\n"); + return ret; + } + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_RELEASE_RESET); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "timeout in releasing reset\n"); + + return ret; +} + +static int acp_init(struct snd_sof_dev *sdev) +{ + int ret; + + /* power on */ + ret = acp_power_on(sdev); + if (ret) { + dev_err(sdev->dev, "ACP power on failed\n"); + return ret; + } + /* Reset */ + return acp_reset(sdev); +} + +int amd_sof_acp_probe(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = to_pci_dev(sdev->dev); + struct acp_dev_data *adata; + const struct sof_amd_acp_desc *chip; + unsigned int addr; + int ret; + + adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data), + GFP_KERNEL); + if (!adata) + return -ENOMEM; + + adata->dev = sdev; + addr = pci_resource_start(pci, ACP_DSP_BAR); + sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR)); + if (!sdev->bar[ACP_DSP_BAR]) { + dev_err(sdev->dev, "ioremap error\n"); + return -ENXIO; + } + + pci_set_master(pci); + + sdev->pdata->hw_pdata = adata; + + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); + return -EIO; + } + + adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL); + if (!adata->smn_dev) { + dev_err(sdev->dev, "Failed to get host bridge device\n"); + return -ENODEV; + } + + sdev->ipc_irq = pci->irq; + ret = request_threaded_irq(sdev->ipc_irq, acp_irq_handler, acp_irq_thread, + IRQF_SHARED, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to register IRQ %d\n", + sdev->ipc_irq); + pci_dev_put(adata->smn_dev); + return ret; + } + + ret = acp_init(sdev); + if (ret < 0) { + free_irq(sdev->ipc_irq, sdev); + pci_dev_put(adata->smn_dev); + return ret; + } + + acp_memory_init(sdev); + + acp_dsp_stream_init(sdev); + + return 0; +} +EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON); + +int amd_sof_acp_remove(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + + if (adata->smn_dev) + pci_dev_put(adata->smn_dev); + + if (sdev->ipc_irq) + free_irq(sdev->ipc_irq, sdev); + + return acp_reset(sdev); +} +EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON); + +MODULE_DESCRIPTION("AMD ACP sof driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h new file mode 100644 index 000000000000..a2f8e4219066 --- /dev/null +++ b/sound/soc/sof/amd/acp.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * + * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + */ + +#ifndef __SOF_AMD_ACP_H +#define __SOF_AMD_ACP_H + +#include "../sof-priv.h" + +#define ACP_MAX_STREAM 8 + +#define ACP_DSP_BAR 0 + +#define ACP_REG_POLL_INTERVAL 500 +#define ACP_REG_POLL_TIMEOUT_US 2000 +#define ACP_DMA_COMPLETE_TIMEOUT_US 5000 + +#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 +#define ACP_PGFSM_STATUS_MASK 0x03 +#define ACP_POWERED_ON 0x00 +#define ACP_ASSERT_RESET 0x01 +#define ACP_RELEASE_RESET 0x00 +#define ACP_SOFT_RESET_DONE_MASK 0x00010001 + +#define ACP_DSP_INTR_EN_MASK 0x00000001 +#define ACP_SRAM_PTE_OFFSET 0x02050000 +#define PAGE_SIZE_4K_ENABLE 0x2 +#define ACP_PAGE_SIZE 0x1000 +#define ACP_DMA_CH_RUN 0x02 +#define ACP_MAX_DESC_CNT 0x02 +#define DSP_FW_RUN_ENABLE 0x01 +#define ACP_SHA_RUN 0x01 +#define ACP_SHA_RESET 0x02 +#define ACP_DMA_CH_RST 0x01 +#define ACP_DMA_CH_GRACEFUL_RST_EN 0x10 +#define ACP_ATU_CACHE_INVALID 0x01 +#define ACP_MAX_DESC 128 +#define ACPBUS_REG_BASE_OFFSET ACP_DMA_CNTL_0 + +#define ACP_DEFAULT_DRAM_LENGTH 0x00080000 +#define ACP_SCRATCH_MEMORY_ADDRESS 0x02050000 +#define ACP_SYSTEM_MEMORY_WINDOW 0x4000000 +#define ACP_IRAM_BASE_ADDRESS 0x000000 +#define ACP_DATA_RAM_BASE_ADDRESS 0x01000000 +#define ACP_DRAM_PAGE_COUNT 128 + +#define ACP_DSP_TO_HOST_IRQ 0x04 + +#define HOST_BRIDGE_CZN 0x1630 +#define ACP_SHA_STAT 0x8000 +#define ACP_PSP_TIMEOUT_COUNTER 5 +#define ACP_EXT_INTR_ERROR_STAT 0x20000000 +#define MP0_C2PMSG_26_REG 0x03810570 +#define MBOX_ACP_SHA_DMA_COMMAND 0x330000 +#define MBOX_READY_MASK 0x80000000 +#define MBOX_STATUS_MASK 0xFFFF + +struct acp_atu_grp_pte { + u32 low; + u32 high; +}; + +union dma_tx_cnt { + struct { + unsigned int count : 19; + unsigned int reserved : 12; + unsigned ioc : 1; + } bitfields, bits; + unsigned int u32_all; + signed int i32_all; +}; + +struct dma_descriptor { + unsigned int src_addr; + unsigned int dest_addr; + union dma_tx_cnt tx_cnt; + unsigned int reserved; +}; + +/* Scratch memory structure for communication b/w host and dsp */ +struct scratch_ipc_conf { + /* DSP mailbox */ + u8 sof_out_box[512]; + /* Host mailbox */ + u8 sof_in_box[512]; + /* Debug memory */ + u8 sof_debug_box[1024]; + /* Exception memory*/ + u8 sof_except_box[1024]; + /* Stream buffer */ + u8 sof_stream_box[1024]; + /* Trace buffer */ + u8 sof_trace_box[1024]; + /* Host msg flag */ + u32 sof_host_msg_write; + /* Host ack flag*/ + u32 sof_host_ack_write; + /* DSP msg flag */ + u32 sof_dsp_msg_write; + /* Dsp ack flag */ + u32 sof_dsp_ack_write; +}; + +struct scratch_reg_conf { + struct scratch_ipc_conf info; + struct acp_atu_grp_pte grp1_pte[16]; + struct acp_atu_grp_pte grp2_pte[16]; + struct acp_atu_grp_pte grp3_pte[16]; + struct acp_atu_grp_pte grp4_pte[16]; + struct acp_atu_grp_pte grp5_pte[16]; + struct acp_atu_grp_pte grp6_pte[16]; + struct acp_atu_grp_pte grp7_pte[16]; + struct acp_atu_grp_pte grp8_pte[16]; + struct dma_descriptor dma_desc[64]; + unsigned int reg_offset[8]; + unsigned int buf_size[8]; + u8 acp_tx_fifo_buf[256]; + u8 acp_rx_fifo_buf[256]; + unsigned int reserve[]; +}; + +struct acp_dsp_stream { + struct list_head list; + struct snd_sof_dev *sdev; + struct snd_pcm_substream *substream; + struct snd_dma_buffer *dmab; + int num_pages; + int stream_tag; + int active; + unsigned int reg_offset; +}; + +/* Common device data struct for ACP devices */ +struct acp_dev_data { + struct snd_sof_dev *dev; + unsigned int fw_bin_size; + unsigned int fw_data_bin_size; + u32 fw_bin_page_count; + dma_addr_t sha_dma_addr; + u8 *bin_buf; + dma_addr_t dma_addr; + u8 *data_buf; + struct dma_descriptor dscr_info[ACP_MAX_DESC]; + struct acp_dsp_stream stream_buf[ACP_MAX_STREAM]; + struct acp_dsp_stream *dtrace_stream; + struct pci_dev *smn_dev; +}; + +void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); +void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes); + +int acp_dma_status(struct acp_dev_data *adata, unsigned char ch); +int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr, + unsigned int dest_addr, int dsp_data_size); +int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, + unsigned int start_addr, unsigned int dest_addr, + unsigned int image_length); + +/* ACP device probe/remove */ +int amd_sof_acp_probe(struct snd_sof_dev *sdev); +int amd_sof_acp_remove(struct snd_sof_dev *sdev); + +/* DSP Loader callbacks */ +int acp_sof_dsp_run(struct snd_sof_dev *sdev); +int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev); +int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type); + +/* Block IO callbacks */ +int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *src, size_t size); +int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *dest, size_t size); + +/* IPC callbacks */ +irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context); +int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + void *p, size_t sz); +int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg); +int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev); +int acp_sof_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id); +int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + const struct sof_ipc_pcm_params_reply *reply); +void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); +void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); + +/* ACP - DSP stream callbacks */ +int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream); +int acp_dsp_stream_init(struct snd_sof_dev *sdev); +struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag); +int acp_dsp_stream_put(struct snd_sof_dev *sdev, struct acp_dsp_stream *acp_stream); + +/* + * DSP PCM Operations. + */ +int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); +int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); +int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params); + +extern const struct snd_sof_dsp_ops sof_renoir_ops; + +/* Machine configuration */ +int snd_amd_acp_find_config(struct pci_dev *pci); + +/* Trace */ +int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); +int acp_sof_trace_release(struct snd_sof_dev *sdev); + +struct sof_amd_acp_desc { + unsigned int host_bridge_id; +}; + +static inline const struct sof_amd_acp_desc *get_chip_info(struct snd_sof_pdata *pdata) +{ + const struct sof_dev_desc *desc = pdata->desc; + + return desc->chip_info; +} +#endif diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c new file mode 100644 index 000000000000..392ffbdf6417 --- /dev/null +++ b/sound/soc/sof/amd/pci-rn.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + +/* + * PCI interface for Renoir ACP device + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <sound/sof.h> +#include <sound/soc-acpi.h> + +#include "../ops.h" +#include "../sof-pci-dev.h" +#include "../../amd/mach-config.h" +#include "acp.h" + +#define ACP3x_REG_START 0x1240000 +#define ACP3x_REG_END 0x125C000 + +static struct platform_device *dmic_dev; +static struct platform_device *pdev; + +static const struct resource renoir_res[] = { + { + .start = 0, + .end = ACP3x_REG_END - ACP3x_REG_START, + .name = "acp_mem", + .flags = IORESOURCE_MEM, + }, + { + .start = 0, + .end = 0, + .name = "acp_dai_irq", + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct sof_amd_acp_desc renoir_chip_info = { + .host_bridge_id = HOST_BRIDGE_CZN, +}; + +static const struct sof_dev_desc renoir_desc = { + .machines = snd_soc_acpi_amd_sof_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &renoir_chip_info, + .default_fw_path = "amd/sof", + .default_tplg_path = "amd/sof-tplg", + .default_fw_filename = "sof-rn.ri", + .nocodec_tplg_filename = "sof-acp.tplg", + .ops = &sof_renoir_ops, +}; + +static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + struct platform_device_info pdevinfo; + struct device *dev = &pci->dev; + const struct resource *res_i2s; + struct resource *res; + unsigned int flag, i, addr; + int ret; + + flag = snd_amd_acp_find_config(pci); + if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) + return -ENODEV; + + ret = sof_pci_probe(pci, pci_id); + if (ret != 0) + return ret; + + dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(dmic_dev)) { + dev_err(dev, "failed to create DMIC device\n"); + sof_pci_remove(pci); + return PTR_ERR(dmic_dev); + } + + /* Register platform device only if flag set to FLAG_AMD_SOF_ONLY_DMIC */ + if (flag != FLAG_AMD_SOF_ONLY_DMIC) + return 0; + + addr = pci_resource_start(pci, 0); + res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL); + if (!res) { + sof_pci_remove(pci); + return -ENOMEM; + } + + res_i2s = renoir_res; + for (i = 0; i < ARRAY_SIZE(renoir_res); i++, res_i2s++) { + res[i].name = res_i2s->name; + res[i].flags = res_i2s->flags; + res[i].start = addr + res_i2s->start; + res[i].end = addr + res_i2s->end; + if (res_i2s->flags == IORESOURCE_IRQ) { + res[i].start = pci->irq; + res[i].end = res[i].start; + } + } + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + + /* + * We have common PCI driver probe for ACP device but we have to support I2S without SOF + * for some distributions. Register platform device that will be used to support non dsp + * ACP's audio ends points on some machines. + */ + + pdevinfo.name = "acp_asoc_renoir"; + pdevinfo.id = 0; + pdevinfo.parent = &pci->dev; + pdevinfo.num_res = ARRAY_SIZE(renoir_res); + pdevinfo.res = &res[0]; + + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) { + dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name); + sof_pci_remove(pci); + platform_device_unregister(dmic_dev); + ret = PTR_ERR(pdev); + } + + return ret; +}; + +static void acp_pci_rn_remove(struct pci_dev *pci) +{ + if (dmic_dev) + platform_device_unregister(dmic_dev); + if (pdev) + platform_device_unregister(pdev); + + return sof_pci_remove(pci); +} + +/* PCI IDs */ +static const struct pci_device_id rn_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), + .driver_data = (unsigned long)&renoir_desc}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, rn_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_amd_rn_driver = { + .name = KBUILD_MODNAME, + .id_table = rn_pci_ids, + .probe = acp_pci_rn_probe, + .remove = acp_pci_rn_remove, +}; +module_pci_driver(snd_sof_pci_amd_rn_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); +MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV); diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c new file mode 100644 index 000000000000..c3ecb9e9d5ba --- /dev/null +++ b/sound/soc/sof/amd/renoir.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> + +/* + * Hardware interface for Audio DSP on Renoir platform + */ + +#include <linux/platform_device.h> +#include <linux/module.h> + +#include "../ops.h" +#include "../sof-audio.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +#define I2S_BT_INSTANCE 0 +#define I2S_SP_INSTANCE 1 +#define PDM_DMIC_INSTANCE 2 + +#define I2S_MODE 0x04 + +static int renoir_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_I2S_PIN_CONFIG); + if (val != I2S_MODE) { + dev_err(sdev->dev, "I2S Mode is not supported (I2S_PIN_CONFIG: %#x)\n", val); + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_dai_driver renoir_sof_dai[] = { + [I2S_BT_INSTANCE] = { + .id = I2S_BT_INSTANCE, + .name = "acp-sof-bt", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S BT controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .probe = &renoir_dai_probe, + }, + + [I2S_SP_INSTANCE] = { + .id = I2S_SP_INSTANCE, + .name = "acp-sof-sp", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S SP controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .probe = &renoir_dai_probe, + }, + + [PDM_DMIC_INSTANCE] = { + .id = PDM_DMIC_INSTANCE, + .name = "acp-sof-dmic", + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + }, + }, +}; + +static struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_soc_acpi_mach *mach; + + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { + dev_warn(sdev->dev, "No matching ASoC machine driver found\n"); + return NULL; + } + + sof_pdata->tplg_filename = mach->sof_tplg_filename; + sof_pdata->fw_filename = mach->fw_filename; + + return mach; +} + +/* AMD Renoir DSP ops */ +const struct snd_sof_dsp_ops sof_renoir_ops = { + /* probe and remove */ + .probe = amd_sof_acp_probe, + .remove = amd_sof_acp_remove, + + /* Register IO */ + .write = sof_io_write, + .read = sof_io_read, + + /* Block IO */ + .block_read = acp_dsp_block_read, + .block_write = acp_dsp_block_write, + + /* Module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + .pre_fw_run = acp_dsp_pre_fw_run, + .get_bar_index = acp_get_bar_index, + + /* DSP core boot */ + .run = acp_sof_dsp_run, + + /*IPC */ + .send_msg = acp_sof_ipc_send_msg, + .ipc_msg_data = acp_sof_ipc_msg_data, + .ipc_pcm_params = acp_sof_ipc_pcm_params, + .get_mailbox_offset = acp_sof_ipc_get_mailbox_offset, + .irq_thread = acp_sof_ipc_irq_thread, + .fw_ready = sof_fw_ready, + + /* DAI drivers */ + .drv = renoir_sof_dai, + .num_drv = ARRAY_SIZE(renoir_sof_dai), + + /* stream callbacks */ + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + /* Machine driver callbacks */ + .machine_select = amd_sof_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + + /* Trace Logger */ + .trace_init = acp_sof_trace_init, + .trace_release = acp_sof_trace_release, +}; +EXPORT_SYMBOL(sof_renoir_ops); + +MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); +MODULE_DESCRIPTION("RENOIR SOF Driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index bb1dfe4f6d40..ef61936dad59 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -69,7 +69,6 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) { struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct snd_soc_component *scomp = scontrol->scomp; - u32 ipc_cmd; int ret; if (!scontrol->comp_data_dirty) @@ -78,20 +77,13 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) if (!pm_runtime_active(scomp->dev)) return; - if (scontrol->cmd == SOF_CTRL_CMD_BINARY) - ipc_cmd = SOF_IPC_COMP_GET_DATA; - else - ipc_cmd = SOF_IPC_COMP_GET_VALUE; - /* set the ABI header values */ cdata->data->magic = SOF_ABI_MAGIC; cdata->data->abi = SOF_ABI_VERSION; /* refresh the component data from DSP */ scontrol->comp_data_dirty = false; - ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd, - SOF_CTRL_TYPE_VALUE_CHAN_GET, - scontrol->cmd, false); + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); if (ret < 0) { dev_err(scomp->dev, "error: failed to get control data: %d\n", ret); /* Set the flag to re-try next time to get the data */ @@ -142,11 +134,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_VALUE, - SOF_CTRL_TYPE_VALUE_CHAN_SET, - SOF_CTRL_CMD_VOLUME, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return change; } @@ -215,11 +203,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_VALUE, - SOF_CTRL_TYPE_VALUE_CHAN_SET, - SOF_CTRL_CMD_SWITCH, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return change; } @@ -264,11 +248,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, /* notify DSP of enum updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_VALUE, - SOF_CTRL_TYPE_VALUE_CHAN_SET, - SOF_CTRL_CMD_ENUM, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return change; } @@ -342,11 +322,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_DATA, - SOF_CTRL_TYPE_DATA_SET, - scontrol->cmd, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return 0; } @@ -391,7 +367,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, } /* Check that header id matches the command */ - if (header.numid != scontrol->cmd) { + if (header.numid != cdata->cmd) { dev_err_ratelimited(scomp->dev, "error: incorrect numid %d\n", header.numid); @@ -422,11 +398,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_DATA, - SOF_CTRL_TYPE_DATA_SET, - scontrol->cmd, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return 0; } @@ -463,8 +435,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ cdata->data->magic = SOF_ABI_MAGIC; cdata->data->abi = SOF_ABI_VERSION; /* get all the component data from DSP */ - ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_GET_DATA, SOF_CTRL_TYPE_DATA_GET, - scontrol->cmd, false); + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); if (ret < 0) goto out; @@ -485,7 +456,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ goto out; } - header.numid = scontrol->cmd; + header.numid = cdata->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) { ret = -EFAULT; @@ -545,7 +516,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, if (data_size > size) return -ENOSPC; - header.numid = scontrol->cmd; + header.numid = cdata->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) return -EFAULT; @@ -600,6 +571,13 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev, bool found = false; int i, type; + if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || + cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { + dev_err(sdev->dev, + "Component data is not supported in control notification\n"); + return; + } + /* Find the swidget first */ list_for_each_entry(swidget, &sdev->widget_list, list) { if (swidget->comp_id == cdata->comp_id) { @@ -666,11 +644,6 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev, expected_size += cdata->num_elems * sizeof(struct sof_ipc_ctrl_value_chan); break; - case SOF_CTRL_TYPE_VALUE_COMP_GET: - case SOF_CTRL_TYPE_VALUE_COMP_SET: - expected_size += cdata->num_elems * - sizeof(struct sof_ipc_ctrl_value_comp); - break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2c3de295f11f..8f32b5b12b3e 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -19,7 +19,7 @@ #endif /* see SOF_DBG_ flags */ -int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE); +static int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE); module_param_named(sof_debug, sof_core_debug, int, 0444); MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); @@ -27,6 +27,22 @@ MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); #define TIMEOUT_DEFAULT_IPC_MS 500 #define TIMEOUT_DEFAULT_BOOT_MS 2000 +/** + * sof_debug_check_flag - check if a given flag(s) is set in sof_core_debug + * @mask: Flag or combination of flags to check + * + * Returns true if all bits set in mask is also set in sof_core_debug, otherwise + * false + */ +bool sof_debug_check_flag(int mask) +{ + if ((sof_core_debug & mask) == mask) + return true; + + return false; +} +EXPORT_SYMBOL(sof_debug_check_flag); + /* * FW Panic/fault handling. */ @@ -52,23 +68,33 @@ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_ASSERT, "assertion failed"}, }; -/* +/** + * sof_print_oops_and_stack - Handle the printing of DSP oops and stack trace + * @sdev: Pointer to the device's sdev + * @level: prink log level to use for the printing + * @panic_code: the panic code + * @tracep_code: tracepoint code + * @oops: Pointer to DSP specific oops data + * @panic_info: Pointer to the received panic information message + * @stack: Pointer to the call stack data + * @stack_words: Number of words in the stack data + * * helper to be called from .dbg_dump callbacks. No error code is * provided, it's left as an exercise for the caller of .dbg_dump * (typically IPC or loader) */ -void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, - u32 tracep_code, void *oops, - struct sof_ipc_panic_info *panic_info, - void *stack, size_t stack_words) +void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level, + u32 panic_code, u32 tracep_code, void *oops, + struct sof_ipc_panic_info *panic_info, + void *stack, size_t stack_words) { u32 code; int i; /* is firmware dead ? */ if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) { - dev_err(sdev->dev, "unexpected fault %#010x trace %#010x\n", - panic_code, tracep_code); + dev_printk(level, sdev->dev, "unexpected fault %#010x trace %#010x\n", + panic_code, tracep_code); return; /* no fault ? */ } @@ -76,54 +102,55 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, for (i = 0; i < ARRAY_SIZE(panic_msg); i++) { if (panic_msg[i].id == code) { - dev_err(sdev->dev, "reason: %s (%#x)\n", panic_msg[i].msg, - code & SOF_IPC_PANIC_CODE_MASK); - dev_err(sdev->dev, "trace point: %#010x\n", tracep_code); + dev_printk(level, sdev->dev, "reason: %s (%#x)\n", + panic_msg[i].msg, code & SOF_IPC_PANIC_CODE_MASK); + dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code); goto out; } } /* unknown error */ - dev_err(sdev->dev, "unknown panic code: %#x\n", code & SOF_IPC_PANIC_CODE_MASK); - dev_err(sdev->dev, "trace point: %#010x\n", tracep_code); + dev_printk(level, sdev->dev, "unknown panic code: %#x\n", + code & SOF_IPC_PANIC_CODE_MASK); + dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code); out: - dev_err(sdev->dev, "panic at %s:%d\n", panic_info->filename, - panic_info->linenum); - sof_oops(sdev, oops); - sof_stack(sdev, oops, stack, stack_words); + dev_printk(level, sdev->dev, "panic at %s:%d\n", panic_info->filename, + panic_info->linenum); + sof_oops(sdev, level, oops); + sof_stack(sdev, level, oops, stack, stack_words); } -EXPORT_SYMBOL(snd_sof_get_status); +EXPORT_SYMBOL(sof_print_oops_and_stack); /* * FW Boot State Transition Diagram * - * +-----------------------------------------------------------------------+ - * | | - * ------------------ ------------------ | - * | | | | | - * | BOOT_FAILED | | READY_FAILED |-------------------------+ | - * | | | | | | - * ------------------ ------------------ | | - * ^ ^ | | - * | | | | - * (FW Boot Timeout) (FW_READY FAIL) | | - * | | | | - * | | | | - * ------------------ | ------------------ | | - * | | | | | | | - * | IN_PROGRESS |---------------+------------->| COMPLETE | | | - * | | (FW Boot OK) (FW_READY OK) | | | | - * ------------------ ------------------ | | - * ^ | | | - * | | | | - * (FW Loading OK) (System Suspend/Runtime Suspend) - * | | | | - * | | | | - * ------------------ ------------------ | | | - * | | | |<-----+ | | - * | PREPARE | | NOT_STARTED |<---------------------+ | - * | | | |<---------------------------+ + * +----------------------------------------------------------------------+ + * | | + * ------------------ ------------------ | + * | | | | | + * | BOOT_FAILED |<-------| READY_FAILED | | + * | |<--+ | | ------------------ | + * ------------------ | ------------------ | | | + * ^ | ^ | CRASHED |---+ | + * | | | | | | | + * (FW Boot Timeout) | (FW_READY FAIL) ------------------ | | + * | | | ^ | | + * | | | |(DSP Panic) | | + * ------------------ | | ------------------ | | + * | | | | | | | | + * | IN_PROGRESS |---------------+------------->| COMPLETE | | | + * | | (FW Boot OK) (FW_READY OK) | | | | + * ------------------ | ------------------ | | + * ^ | | | | + * | | | | | + * (FW Loading OK) | (System Suspend/Runtime Suspend) + * | | | | | + * | (FW Loading Fail) | | | + * ------------------ | ------------------ | | | + * | | | | |<-----+ | | + * | PREPARE |---+ | NOT_STARTED |<---------------------+ | + * | | | |<--------------------------+ * ------------------ ------------------ * | ^ | ^ * | | | | @@ -186,6 +213,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); goto fw_load_err; } @@ -199,10 +227,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); goto fw_run_err; } - if (sof_core_debug & SOF_DBG_ENABLE_TRACE) { + if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) { sdev->dtrace_is_supported = true; /* init DMA trace */ @@ -362,7 +391,15 @@ int snd_sof_device_remove(struct device *dev) if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); + /* + * Unregister machine driver. This will unbind the snd_card which + * will remove the component driver and unload the topology + * before freeing the snd_card. + */ + snd_sof_machine_unregister(sdev, pdata); + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { + snd_sof_free_trace(sdev); ret = snd_sof_dsp_power_down_notify(sdev); if (ret < 0) dev_warn(dev, "error: %d failed to prepare DSP for device removal", @@ -370,17 +407,9 @@ int snd_sof_device_remove(struct device *dev) snd_sof_ipc_free(sdev); snd_sof_free_debug(sdev); - snd_sof_free_trace(sdev); } /* - * Unregister machine driver. This will unbind the snd_card which - * will remove the component driver and unload the topology - * before freeing the snd_card. - */ - snd_sof_machine_unregister(sdev, pdata); - - /* * Unregistering the machine driver results in unloading the topology. * Some widgets, ex: scheduler, attempt to power down the core they are * scheduled on, when they are unloaded. Therefore, the DSP must be diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index dc1df5fb7b4c..6d6757075f7c 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -336,6 +336,104 @@ static int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev, } #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) +static ssize_t msg_inject_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct sof_ipc_reply *rhdr = dfse->msg_inject_rx; + + if (!rhdr->hdr.size || !count || *ppos) + return 0; + + if (count > rhdr->hdr.size) + count = rhdr->hdr.size; + + if (copy_to_user(buffer, dfse->msg_inject_rx, count)) + return -EFAULT; + + *ppos += count; + return count; +} + +static ssize_t msg_inject_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + struct sof_ipc_cmd_hdr *hdr = dfse->msg_inject_tx; + size_t size; + int ret, err; + + if (*ppos) + return 0; + + size = simple_write_to_buffer(dfse->msg_inject_tx, SOF_IPC_MSG_MAX_SIZE, + ppos, buffer, count); + if (size != count) + return size > 0 ? -EFAULT : size; + + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(sdev->dev, "%s: DSP resume failed: %d\n", + __func__, ret); + pm_runtime_put_noidle(sdev->dev); + goto out; + } + + /* send the message */ + memset(dfse->msg_inject_rx, 0, SOF_IPC_MSG_MAX_SIZE); + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, dfse->msg_inject_tx, count, + dfse->msg_inject_rx, SOF_IPC_MSG_MAX_SIZE); + + pm_runtime_mark_last_busy(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err_ratelimited(sdev->dev, "%s: DSP idle failed: %d\n", + __func__, err); + + /* return size if test is successful */ + if (ret >= 0) + ret = size; + +out: + return ret; +} + +static const struct file_operations msg_inject_fops = { + .open = simple_open, + .read = msg_inject_read, + .write = msg_inject_write, + .llseek = default_llseek, +}; + +static int snd_sof_debugfs_msg_inject_item(struct snd_sof_dev *sdev, + const char *name, mode_t mode, + const struct file_operations *fops) +{ + struct snd_sof_dfsentry *dfse; + + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + /* pre allocate the tx and rx buffers */ + dfse->msg_inject_tx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + dfse->msg_inject_rx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!dfse->msg_inject_tx || !dfse->msg_inject_rx) + return -ENOMEM; + + dfse->type = SOF_DFSENTRY_TYPE_BUF; + dfse->sdev = sdev; + + debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops); + /* add to dfsentry list */ + list_add(&dfse->list, &sdev->dfsentry_list); + + return 0; +} +#endif + static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { @@ -812,6 +910,15 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) return err; #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) + err = snd_sof_debugfs_msg_inject_item(sdev, "ipc_msg_inject", 0644, + &msg_inject_fops); + + /* errors are only due to memory allocation, not debugfs */ + if (err < 0) + return err; +#endif + return 0; } EXPORT_SYMBOL_GPL(snd_sof_dbg_init); @@ -823,7 +930,7 @@ void snd_sof_free_debug(struct snd_sof_dev *sdev) EXPORT_SYMBOL_GPL(snd_sof_free_debug); static const struct soc_fw_state_info { - enum snd_sof_fw_state state; + enum sof_fw_state state; const char *name; } fw_state_dbg[] = { {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"}, @@ -831,37 +938,47 @@ static const struct soc_fw_state_info { {SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"}, {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"}, {SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"}, + {SOF_FW_BOOT_READY_OK, "SOF_FW_BOOT_READY_OK"}, {SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"}, + {SOF_FW_CRASHED, "SOF_FW_CRASHED"}, }; -static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev) +static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *level) { int i; for (i = 0; i < ARRAY_SIZE(fw_state_dbg); i++) { if (sdev->fw_state == fw_state_dbg[i].state) { - dev_err(sdev->dev, "fw_state: %s (%d)\n", fw_state_dbg[i].name, i); + dev_printk(level, sdev->dev, "fw_state: %s (%d)\n", + fw_state_dbg[i].name, i); return; } } - dev_err(sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); + dev_printk(level, sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); } -void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) +void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags) { - bool print_all = !!(sof_core_debug & SOF_DBG_PRINT_ALL_DUMPS); + char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; + bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all) return; if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { - dev_err(sdev->dev, "------------[ DSP dump start ]------------\n"); - snd_sof_dbg_print_fw_state(sdev); + dev_printk(level, sdev->dev, + "------------[ DSP dump start ]------------\n"); + if (msg) + dev_printk(level, sdev->dev, "%s\n", msg); + snd_sof_dbg_print_fw_state(sdev, level); sof_ops(sdev)->dbg_dump(sdev, flags); - dev_err(sdev->dev, "------------[ DSP dump end ]------------\n"); + dev_printk(level, sdev->dev, + "------------[ DSP dump end ]------------\n"); if (!print_all) sdev->dbg_dump_printed = true; + } else if (msg) { + dev_printk(level, sdev->dev, "%s\n", msg); } } EXPORT_SYMBOL(snd_sof_dsp_dbg_dump); @@ -872,7 +989,7 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) dev_err(sdev->dev, "------------[ IPC dump start ]------------\n"); sof_ops(sdev)->ipc_dump(sdev); dev_err(sdev->dev, "------------[ IPC dump end ]------------\n"); - if (!(sof_core_debug & SOF_DBG_PRINT_ALL_DUMPS)) + if (!sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) sdev->ipc_dump_printed = true; } } @@ -880,7 +997,7 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) { if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || - (sof_core_debug & SOF_DBG_RETAIN_CTX)) { + sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) { /* should we prevent DSP entering D3 ? */ if (!sdev->ipc_dump_printed) dev_info(sdev->dev, @@ -890,7 +1007,8 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) /* dump vital information to the logs */ snd_sof_ipc_dump(sdev); - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + snd_sof_dsp_dbg_dump(sdev, "Firmware exception", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_handle_fw_exception); diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig index 34cf228c188f..9b8d5bb1e449 100644 --- a/sound/soc/sof/imx/Kconfig +++ b/sound/soc/sof/imx/Kconfig @@ -11,53 +11,33 @@ config SND_SOC_SOF_IMX_TOPLEVEL if SND_SOC_SOF_IMX_TOPLEVEL -config SND_SOC_SOF_IMX_OF - def_tristate SND_SOC_SOF_OF - select SND_SOC_SOF_IMX8 if SND_SOC_SOF_IMX8_SUPPORT - select SND_SOC_SOF_IMX8M if SND_SOC_SOF_IMX8M_SUPPORT - help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. - config SND_SOC_SOF_IMX_COMMON tristate + select SND_SOC_SOF_OF_DEV + select SND_SOC_SOF + select SND_SOC_SOF_XTENSA + select SND_SOC_SOF_COMPRESS help This option is not user-selectable but automagically handled by 'select' statements at a higher level. -config SND_SOC_SOF_IMX8_SUPPORT - bool "SOF support for i.MX8" - depends on IMX_SCU=y || IMX_SCU=SND_SOC_SOF_IMX_OF - depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_IMX_OF +config SND_SOC_SOF_IMX8 + tristate "SOF support for i.MX8" + depends on IMX_SCU + depends on IMX_DSP + select SND_SOC_SOF_IMX_COMMON help This adds support for Sound Open Firmware for NXP i.MX8 platforms. Say Y if you have such a device. If unsure select "N". -config SND_SOC_SOF_IMX8 - tristate +config SND_SOC_SOF_IMX8M + tristate "SOF support for i.MX8M" + depends on IMX_DSP select SND_SOC_SOF_IMX_COMMON - select SND_SOC_SOF_XTENSA - select SND_SOC_SOF_COMPRESS - help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. - -config SND_SOC_SOF_IMX8M_SUPPORT - bool "SOF support for i.MX8M" - depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_OF help This adds support for Sound Open Firmware for NXP i.MX8M platforms. Say Y if you have such a device. If unsure select "N". -config SND_SOC_SOF_IMX8M - tristate - select SND_SOC_SOF_IMX_COMMON - select SND_SOC_SOF_XTENSA - select SND_SOC_SOF_COMPRESS - help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. - -endif ## SND_SOC_SOF_IMX_IMX_TOPLEVEL +endif ## SND_SOC_SOF_IMX_TOPLEVEL diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 8826ef94f04a..36e3d414a18f 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -69,9 +69,33 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags) IMX8_STACK_DUMP_SIZE); /* Print the information to the console */ - snd_sof_get_status(sdev, status, status, &xoops, &panic_info, stack, - IMX8_STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, status, &xoops, + &panic_info, stack, IMX8_STACK_DUMP_SIZE); } EXPORT_SYMBOL(imx8_dump); +int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + int ret; + + ret = devm_clk_bulk_get(sdev->dev, clks->num_dsp_clks, clks->dsp_clks); + if (ret) + dev_err(sdev->dev, "Failed to request DSP clocks\n"); + + return ret; +} +EXPORT_SYMBOL(imx8_parse_clocks); + +int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + return clk_bulk_prepare_enable(clks->num_dsp_clks, clks->dsp_clks); +} +EXPORT_SYMBOL(imx8_enable_clocks); + +void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + clk_bulk_disable_unprepare(clks->num_dsp_clks, clks->dsp_clks); +} +EXPORT_SYMBOL(imx8_disable_clocks); + MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 1cc7d6704182..ec4b3a5c7496 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -3,6 +3,8 @@ #ifndef __IMX_COMMON_H__ #define __IMX_COMMON_H__ +#include <linux/clk.h> + #define EXCEPT_MAX_HDR_SIZE 0x400 #define IMX8_STACK_DUMP_SIZE 32 @@ -13,4 +15,13 @@ void imx8_get_registers(struct snd_sof_dev *sdev, void imx8_dump(struct snd_sof_dev *sdev, u32 flags); +struct imx_clocks { + struct clk_bulk_data *dsp_clks; + int num_dsp_clks; +}; + +int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); +int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); +void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); + #endif diff --git a/sound/soc/sof/imx/imx-ops.h b/sound/soc/sof/imx/imx-ops.h deleted file mode 100644 index 24235ef8c8fa..000000000000 --- a/sound/soc/sof/imx/imx-ops.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ - -#ifndef __IMX_OPS_H__ -#define __IMX_OPS_H__ - -extern struct snd_sof_dsp_ops sof_imx8_ops; -extern struct snd_sof_dsp_ops sof_imx8x_ops; -extern struct snd_sof_dsp_ops sof_imx8m_ops; - -#endif diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index dd59a74480d6..f6baecbb57fb 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -21,8 +21,8 @@ #include <linux/firmware/imx/svc/misc.h> #include <dt-bindings/firmware/imx/rsrc.h> #include "../ops.h" +#include "../sof-of-dev.h" #include "imx-common.h" -#include "imx-ops.h" /* DSP memories */ #define IRAM_OFFSET 0x10000 @@ -41,6 +41,13 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 +/* DSP clocks */ +static struct clk_bulk_data imx8_dsp_clks[] = { + { .id = "ipg" }, + { .id = "ocram" }, + { .id = "core" }, +}; + struct imx8_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -57,42 +64,9 @@ struct imx8_priv { struct device **pd_dev; struct device_link **link; + struct imx_clocks *clks; }; -static void imx8_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; @@ -109,8 +83,7 @@ static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc) unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - imx8_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } @@ -124,7 +97,7 @@ static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc) /* Check to see if the message is a panic code (0x0dead***) */ if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) - snd_sof_dsp_panic(priv->sdev, p); + snd_sof_dsp_panic(priv->sdev, p, true); else snd_sof_ipc_msgs_rx(priv->sdev); } @@ -223,6 +196,11 @@ static int imx8_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; + priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + + sdev->num_cores = 1; sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -335,6 +313,18 @@ static int imx8_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET; + /* init clocks info */ + priv->clks->dsp_clks = imx8_dsp_clks; + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8_dsp_clks); + + ret = imx8_parse_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + return 0; exit_pdev_unregister: @@ -353,6 +343,7 @@ static int imx8_remove(struct snd_sof_dev *sdev) struct imx8_priv *priv = sdev->pdata->hw_pdata; int i; + imx8_disable_clocks(sdev, priv->clks); platform_device_unregister(priv->ipc_dev); for (i = 0; i < priv->num_domains; i++) { @@ -376,6 +367,92 @@ static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type) } } +static void imx8_suspend(struct snd_sof_dev *sdev) +{ + int i; + struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_free_channel(priv->dsp_ipc, i); + + imx8_disable_clocks(sdev, priv->clks); +} + +static int imx8_resume(struct snd_sof_dev *sdev) +{ + struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; + int ret; + int i; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + return ret; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_request_channel(priv->dsp_ipc, i); + + return 0; +} + +static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8_resume(sdev); + if (ret < 0) + return ret; + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D3, + }; + + imx8_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = target_state, + }; + + if (!pm_runtime_suspended(sdev->dev)) + imx8_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8_resume(sdev); + if (ret < 0) + return ret; + + if (pm_runtime_suspended(sdev->dev)) { + pm_runtime_disable(sdev->dev); + pm_runtime_set_active(sdev->dev); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_enable(sdev->dev); + pm_runtime_idle(sdev->dev); + } + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + static struct snd_soc_dai_driver imx8_dai[] = { { .name = "esai0", @@ -401,8 +478,16 @@ static struct snd_soc_dai_driver imx8_dai[] = { }, }; +static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + sdev->dsp_power_state = *target_state; + + return 0; +} + /* i.MX8 ops */ -struct snd_sof_dsp_ops sof_imx8_ops = { +static const struct snd_sof_dsp_ops sof_imx8_ops = { /* probe and remove */ .probe = imx8_probe, .remove = imx8_remove, @@ -453,11 +538,19 @@ struct snd_sof_dsp_ops sof_imx8_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + /* PM */ + .runtime_suspend = imx8_dsp_runtime_suspend, + .runtime_resume = imx8_dsp_runtime_resume, + + .suspend = imx8_dsp_suspend, + .resume = imx8_dsp_resume, + + .set_power_state = imx8_dsp_set_power_state, }; -EXPORT_SYMBOL(sof_imx8_ops); /* i.MX8X ops */ -struct snd_sof_dsp_ops sof_imx8x_ops = { +static const struct snd_sof_dsp_ops sof_imx8x_ops = { /* probe and remove */ .probe = imx8_probe, .remove = imx8_remove, @@ -502,6 +595,15 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), + /* PM */ + .runtime_suspend = imx8_dsp_runtime_suspend, + .runtime_resume = imx8_dsp_runtime_resume, + + .suspend = imx8_dsp_suspend, + .resume = imx8_dsp_resume, + + .set_power_state = imx8_dsp_set_power_state, + /* ALSA HW info flags */ .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | @@ -509,7 +611,41 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP }; -EXPORT_SYMBOL(sof_imx8x_ops); + +static struct sof_dev_desc sof_of_imx8qxp_desc = { + .default_fw_path = "imx/sof", + .default_tplg_path = "imx/sof-tplg", + .default_fw_filename = "sof-imx8x.ri", + .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", + .ops = &sof_imx8x_ops, +}; + +static struct sof_dev_desc sof_of_imx8qm_desc = { + .default_fw_path = "imx/sof", + .default_tplg_path = "imx/sof-tplg", + .default_fw_filename = "sof-imx8.ri", + .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", + .ops = &sof_imx8_ops, +}; + +static const struct of_device_id sof_of_imx8_ids[] = { + { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc}, + { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_imx8_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_imx8_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-imx8", + .pm = &sof_of_pm, + .of_match_table = sof_of_imx8_ids, + }, +}; +module_platform_driver(snd_sof_of_imx8_driver); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index e4618980cf8b..788e77bcb603 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -6,10 +6,13 @@ // // Hardware interface for audio DSP on i.MX8M +#include <linux/bits.h> #include <linux/firmware.h> +#include <linux/mfd/syscon.h> #include <linux/of_platform.h> #include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/regmap.h> #include <linux/module.h> #include <sound/sof.h> @@ -17,12 +20,32 @@ #include <linux/firmware/imx/dsp.h> #include "../ops.h" +#include "../sof-of-dev.h" #include "imx-common.h" -#include "imx-ops.h" #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 +static struct clk_bulk_data imx8m_dsp_clks[] = { + { .id = "ipg" }, + { .id = "ocram" }, + { .id = "core" }, +}; + +/* DAP registers */ +#define IMX8M_DAP_DEBUG 0x28800000 +#define IMX8M_DAP_DEBUG_SIZE (64 * 1024) +#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020) +#define IMX8M_PWRCTL_CORERESET BIT(16) + +/* DSP audio mix registers */ +#define AudioDSP_REG0 0x100 +#define AudioDSP_REG1 0x104 +#define AudioDSP_REG2 0x108 +#define AudioDSP_REG3 0x10c + +#define AudioDSP_REG2_RUNSTALL BIT(5) + struct imx8m_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -30,41 +53,12 @@ struct imx8m_priv { /* DSP IPC handler */ struct imx_dsp_ipc *dsp_ipc; struct platform_device *ipc_dev; -}; - -static void imx8m_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + struct imx_clocks *clks; - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} + void __iomem *dap; + struct regmap *regmap; +}; static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) { @@ -82,8 +76,7 @@ static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc) unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - imx8m_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } @@ -97,7 +90,7 @@ static void imx8m_dsp_handle_request(struct imx_dsp_ipc *ipc) /* Check to see if the message is a panic code (0x0dead***) */ if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) - snd_sof_dsp_panic(priv->sdev, p); + snd_sof_dsp_panic(priv->sdev, p, true); else snd_sof_ipc_msgs_rx(priv->sdev); } @@ -123,7 +116,34 @@ static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) */ static int imx8m_run(struct snd_sof_dev *sdev) { - /* TODO: start DSP using Audio MIX bits */ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + + regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0); + + return 0; +} + +static int imx8m_reset(struct snd_sof_dev *sdev) +{ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + u32 pwrctl; + + /* put DSP into reset and stall */ + pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); + pwrctl |= IMX8M_PWRCTL_CORERESET; + writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); + + /* keep reset asserted for 10 cycles */ + usleep_range(1, 2); + + regmap_update_bits(priv->regmap, AudioDSP_REG2, + AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL); + + /* take the DSP out of reset and keep stalled for FW loading */ + pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); + pwrctl &= ~IMX8M_PWRCTL_CORERESET; + writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); + return 0; } @@ -143,6 +163,11 @@ static int imx8m_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; + priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + + sdev->num_cores = 1; sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -175,6 +200,13 @@ static int imx8m_probe(struct snd_sof_dev *sdev) goto exit_pdev_unregister; } + priv->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE); + if (!priv->dap) { + dev_err(sdev->dev, "error: failed to map DAP debug memory area"); + ret = -ENODEV; + goto exit_pdev_unregister; + } + sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", @@ -210,6 +242,25 @@ static int imx8m_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET; + priv->regmap = syscon_regmap_lookup_by_compatible("fsl,dsp-ctrl"); + if (IS_ERR(priv->regmap)) { + dev_err(sdev->dev, "cannot find dsp-ctrl registers"); + ret = PTR_ERR(priv->regmap); + goto exit_pdev_unregister; + } + + /* init clocks info */ + priv->clks->dsp_clks = imx8m_dsp_clks; + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8m_dsp_clks); + + ret = imx8_parse_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + return 0; exit_pdev_unregister: @@ -221,6 +272,7 @@ static int imx8m_remove(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = sdev->pdata->hw_pdata; + imx8_disable_clocks(sdev, priv->clks); platform_device_unregister(priv->ipc_dev); return 0; @@ -264,13 +316,108 @@ static struct snd_soc_dai_driver imx8m_dai[] = { }, }; +static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + sdev->dsp_power_state = *target_state; + + return 0; +} + +static int imx8m_resume(struct snd_sof_dev *sdev) +{ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + int ret; + int i; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + return ret; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_request_channel(priv->dsp_ipc, i); + + return 0; +} + +static void imx8m_suspend(struct snd_sof_dev *sdev) +{ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + int i; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_free_channel(priv->dsp_ipc, i); + + imx8_disable_clocks(sdev, priv->clks); +} + +static int imx8m_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8m_resume(sdev); + if (ret < 0) + return ret; + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8m_dsp_runtime_suspend(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D3, + }; + + imx8m_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8m_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8m_resume(sdev); + if (ret < 0) + return ret; + + if (pm_runtime_suspended(sdev->dev)) { + pm_runtime_disable(sdev->dev); + pm_runtime_set_active(sdev->dev); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_enable(sdev->dev); + pm_runtime_idle(sdev->dev); + } + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = target_state, + }; + + if (!pm_runtime_suspended(sdev->dev)) + imx8m_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + /* i.MX8 ops */ -struct snd_sof_dsp_ops sof_imx8m_ops = { +static const struct snd_sof_dsp_ops sof_imx8m_ops = { /* probe and remove */ .probe = imx8m_probe, .remove = imx8m_remove, /* DSP core boot */ .run = imx8m_run, + .reset = imx8m_reset, /* Block IO */ .block_read = sof_block_read, @@ -309,13 +456,46 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { .drv = imx8m_dai, .num_drv = ARRAY_SIZE(imx8m_dai), + .suspend = imx8m_dsp_suspend, + .resume = imx8m_dsp_resume, + + .runtime_suspend = imx8m_dsp_runtime_suspend, + .runtime_resume = imx8m_dsp_runtime_resume, + + .set_power_state = imx8m_dsp_set_power_state, + .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; -EXPORT_SYMBOL(sof_imx8m_ops); + +static struct sof_dev_desc sof_of_imx8mp_desc = { + .default_fw_path = "imx/sof", + .default_tplg_path = "imx/sof-tplg", + .default_fw_filename = "sof-imx8m.ri", + .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", + .ops = &sof_imx8m_ops, +}; + +static const struct of_device_id sof_of_imx8m_ids[] = { + { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_imx8m_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_imx8m_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-imx8m", + .pm = &sof_of_pm, + .of_match_table = sof_of_imx8m_ids, + }, +}; +module_platform_driver(snd_sof_of_imx8m_driver); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 917f78cf6daf..810b8b6748a0 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -78,6 +78,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ @@ -101,9 +102,8 @@ const struct snd_sof_dsp_ops sof_apl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ + .core_get = hda_dsp_core_get, /* trace callback */ .trace_init = hda_dsp_trace_init, @@ -147,5 +147,6 @@ const struct sof_intel_dsp_desc apl_chip_info = { .rom_init_timeout = 150, .ssp_count = APL_SSP_COUNT, .ssp_base_offset = APL_SSP_BASE_OFFSET, + .quirks = SOF_INTEL_PROCEN_FMT_QUIRK, }; EXPORT_SYMBOL_NS(apl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index 74c630bb9847..ff5900b155dc 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -27,7 +27,6 @@ static void atom_host_done(struct snd_sof_dev *sdev); static void atom_dsp_done(struct snd_sof_dev *sdev); -static void atom_get_reply(struct snd_sof_dev *sdev); /* * Debug @@ -71,8 +70,8 @@ void atom_dump(struct snd_sof_dev *sdev, u32 flags) panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX); atom_get_registers(sdev, &xoops, &panic_info, stack, STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, - STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops, + &panic_info, stack, STACK_DUMP_SIZE); /* provide some context for firmware debug */ imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX); @@ -154,8 +153,7 @@ irqreturn_t atom_irq_thread(int irq, void *context) * because the done bit can't be set in cmd_done function * which is triggered by msg */ - atom_get_reply(sdev); - snd_sof_ipc_reply(sdev, ipcx); + snd_sof_ipc_process_reply(sdev, ipcx); atom_dsp_done(sdev); @@ -167,8 +165,8 @@ irqreturn_t atom_irq_thread(int irq, void *context) /* Handle messages from DSP Core */ if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + - MBOX_OFFSET); + snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + MBOX_OFFSET, + true); } else { snd_sof_ipc_msgs_rx(sdev); } @@ -195,45 +193,6 @@ int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) } EXPORT_SYMBOL_NS(atom_send_msg, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); -static void atom_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - /* - * Sometimes, there is unexpected reply ipc arriving. The reply - * ipc belongs to none of the ipcs sent from driver. - * In this case, the driver must ignore the ipc. - */ - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - int atom_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; @@ -334,7 +293,7 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev, return tplg_filename; } -void atom_machine_select(struct snd_sof_dev *sdev) +struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -345,7 +304,7 @@ void atom_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); - return; + return NULL; } pdev = to_platform_device(sdev->dev); @@ -363,12 +322,13 @@ void atom_machine_select(struct snd_sof_dev *sdev) if (!tplg_filename) { dev_dbg(sdev->dev, "error: no topology filename\n"); - return; + return NULL; } sof_pdata->tplg_filename = tplg_filename; mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; - sof_pdata->machine = mach; + + return mach; } EXPORT_SYMBOL_NS(atom_machine_select, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); @@ -443,14 +403,14 @@ struct snd_soc_dai_driver atom_dai[] = { }; EXPORT_SYMBOL_NS(atom_dai, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); -void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, +void atom_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; diff --git a/sound/soc/sof/intel/atom.h b/sound/soc/sof/intel/atom.h index 96a462c7a2e5..b965e5e080a6 100644 --- a/sound/soc/sof/intel/atom.h +++ b/sound/soc/sof/intel/atom.h @@ -65,8 +65,8 @@ int atom_run(struct snd_sof_dev *sdev); int atom_reset(struct snd_sof_dev *sdev); void atom_dump(struct snd_sof_dev *sdev, u32 flags); -void atom_machine_select(struct snd_sof_dev *sdev); -void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, +struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev); +void atom_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); extern struct snd_soc_dai_driver atom_dai[]; diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 2c09a523288e..d627b7498d5e 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -75,7 +75,6 @@ static const struct snd_sof_debugfs_map bdw_debugfs[] = { static void bdw_host_done(struct snd_sof_dev *sdev); static void bdw_dsp_done(struct snd_sof_dev *sdev); -static void bdw_get_reply(struct snd_sof_dev *sdev); /* * DSP Control. @@ -259,8 +258,8 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) panic = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); bdw_get_registers(sdev, &xoops, &panic_info, stack, BDW_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, - BDW_STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops, + &panic_info, stack, BDW_STACK_DUMP_SIZE); /* provide some context for firmware debug */ imrx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRX); @@ -326,8 +325,7 @@ static irqreturn_t bdw_irq_thread(int irq, void *context) * because the done bit can't be set in cmd_done function * which is triggered by msg */ - bdw_get_reply(sdev); - snd_sof_ipc_reply(sdev, ipcx); + snd_sof_ipc_process_reply(sdev, ipcx); bdw_dsp_done(sdev); @@ -346,8 +344,8 @@ static irqreturn_t bdw_irq_thread(int irq, void *context) /* Handle messages from DSP Core */ if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + - MBOX_OFFSET); + snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + MBOX_OFFSET, + true); } else { snd_sof_ipc_msgs_rx(sdev); } @@ -372,45 +370,6 @@ static int bdw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) return 0; } -static void bdw_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - /* - * Sometimes, there is unexpected reply ipc arriving. The reply - * ipc belongs to none of the ipcs sent from driver. - * In this case, the driver must ignore the ipc. - */ - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static int bdw_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; @@ -453,10 +412,19 @@ static int bdw_probe(struct snd_sof_dev *sdev) const struct sof_dev_desc *desc = pdata->desc; struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + const struct sof_intel_dsp_desc *chip; struct resource *mmio; u32 base, size; int ret; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "error: no such device supported\n"); + return -EIO; + } + + sdev->num_cores = chip->cores_num; + /* LPE base */ mmio = platform_get_resource(pdev, IORESOURCE_MEM, desc->resindex_lpe_base); @@ -541,7 +509,7 @@ static int bdw_probe(struct snd_sof_dev *sdev) return ret; } -static void bdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *bdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -550,22 +518,23 @@ static void bdw_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); - return; + return NULL; } sof_pdata->tplg_filename = mach->sof_tplg_filename; mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; - sof_pdata->machine = mach; + + return mach; } -static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach, +static void bdw_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index e2fa08f1ae74..dcfeaedb8fd5 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -113,10 +113,19 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev) const struct sof_dev_desc *desc = pdata->desc; struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + const struct sof_intel_dsp_desc *chip; struct resource *mmio; u32 base, size; int ret; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "error: no such device supported\n"); + return -EIO; + } + + sdev->num_cores = chip->cores_num; + /* DSP DMA can only access low 31 bits of host memory */ ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); if (ret < 0) { diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 3957e2b3db32..e615125d575e 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -82,9 +82,24 @@ irqreturn_t cnl_ipc_irq_thread(int irq, void *context) msg, msg_ext); /* handle messages from DSP */ - if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == - SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + bool non_recoverable = true; + + /* + * This is a PANIC message! + * + * If it is arriving during firmware boot and it is not + * the last boot attempt then change the non_recoverable + * to false as the DSP might be able to boot in the next + * iteration(s) + */ + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS && + hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS) + non_recoverable = false; + + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext), + non_recoverable); } else { snd_sof_ipc_msgs_rx(sdev); } @@ -283,6 +298,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ @@ -303,9 +319,8 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ + .core_get = hda_dsp_core_get, /* firmware run */ .run = hda_dsp_cl_boot_firmware, @@ -358,6 +373,13 @@ const struct sof_intel_dsp_desc cnl_chip_info = { }; EXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); +/* + * JasperLake is technically derived from IceLake, and should be in + * described in icl.c. However since JasperLake was designed with + * two cores, it cannot support the IceLake-specific power-up sequences + * which rely on core3. To simplify, JasperLake uses the CannonLake ops and + * is described in cnl.c + */ const struct sof_intel_dsp_desc jsl_chip_info = { /* Jasperlake */ .cores_num = 2, diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 13cd96e6724a..2f3f4a733d9e 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -20,9 +20,10 @@ #include "../../codecs/hdac_hda.h" #endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ +#define CODEC_PROBE_RETRIES 3 + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) #define IDISP_VID_INTEL 0x80860000 -#define CODEC_PROBE_RETRIES 3 /* load the legacy HDA codec driver */ static int request_codec_module(struct hda_codec *codec) diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index fa5f0a718901..0c29bb196e59 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -353,7 +353,7 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev) snd_hdac_bus_stop_cmd_io(bus); #endif /* disable position buffer */ - if (bus->posbuf.addr) { + if (bus->use_posbuf && bus->posbuf.addr) { snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, 0); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 76579383d290..cd12589355ef 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -21,8 +21,6 @@ #endif struct hda_pipe_params { - u8 host_dma_id; - u8 link_dma_id; u32 ch; u32 s_freq; u32 s_fmt; @@ -30,7 +28,6 @@ struct hda_pipe_params { snd_pcm_format_t format; int link_index; int stream; - unsigned int host_bps; unsigned int link_bps; }; @@ -182,24 +179,6 @@ static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widg return config; } -static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, - struct snd_soc_dapm_widget *w, int channel) -{ - struct snd_sof_dev *sdev = hda_stream->sdev; - struct sof_ipc_dai_config *config; - struct sof_ipc_reply reply; - - config = hda_dai_update_config(w, channel); - if (!config) { - dev_err(sdev->dev, "error: no config for DAI %s\n", w->name); - return -ENOENT; - } - - /* send DAI_CONFIG IPC */ - return sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, - &reply, sizeof(reply)); -} - static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, struct snd_soc_dapm_widget *w, int channel, bool widget_setup) @@ -215,9 +194,9 @@ static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, /* set up/free DAI widget and send DAI_CONFIG IPC */ if (widget_setup) - return hda_ctrl_dai_widget_setup(w); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP); - return hda_ctrl_dai_widget_free(w); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int hda_link_hw_params(struct snd_pcm_substream *substream, @@ -264,17 +243,13 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, if (!link) return -EINVAL; - /* set the stream tag in the codec dai dma params */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); - else - snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); + /* set the hdac_stream in the codec dai */ + snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; - p_params.link_dma_id = stream_tag - 1; p_params.link_index = link->index; p_params.format = params_format(params); @@ -305,6 +280,36 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, dai); } +static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *component = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_ipc_dai_config *config; + struct snd_sof_dai *sof_dai; + struct sof_ipc_reply reply; + int ret; + + sof_dai = swidget->private; + + if (!sof_dai || !sof_dai->dai_config) { + dev_err(sdev->dev, "No config for DAI %s\n", w->name); + return -EINVAL; + } + + config = &sof_dai->dai_config[sof_dai->current_config]; + + /* set PAUSE command flag */ + config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE); + + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name); + + return ret; +} + static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -330,33 +335,22 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, hda_stream = hstream_to_sof_hda_stream(link_dev); dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); - switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: - /* set up hw_params */ - ret = hda_link_pcm_prepare(substream, dai); - if (ret < 0) { - dev_err(dai->dev, - "error: setting up hw_params during resume\n"); - return ret; - } - fallthrough; + w = snd_soc_dai_get_widget(dai, substream->stream); + + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_hdac_ext_link_stream_start(link_dev); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + snd_hdac_ext_link_stream_clear(link_dev); /* - * clear link DMA channel. It will be assigned when - * hw_params is set up again after resume. + * free DAI widget during stop/suspend to keep widget use_count's balanced. */ - ret = hda_link_config_ipc(hda_stream, w, DMA_CHAN_INVALID); + ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false); if (ret < 0) return ret; @@ -366,10 +360,13 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, } link_dev->link_prepared = 0; - - fallthrough; + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_hdac_ext_link_stream_clear(link_dev); + + ret = hda_link_dai_config_pause_push_ipc(w); + if (ret < 0) + return ret; break; default: return -EINVAL; @@ -470,9 +467,9 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd return 0; if (setup) - return hda_ctrl_dai_widget_setup(w); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); - return hda_ctrl_dai_widget_free(w); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int ssp_dai_startup(struct snd_pcm_substream *substream, diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 287dc0eb6686..916a257ea96b 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -614,7 +614,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_bus *bus = sof_to_bus(sdev); #endif - int ret; + int ret, j; hda_sdw_int_enable(sdev, false); @@ -629,13 +629,17 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #endif /* power down DSP */ - ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask); + ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); if (ret < 0) { dev_err(sdev->dev, "error: failed to power down core during suspend\n"); return ret; } + /* reset ref counts for all cores */ + for (j = 0; j < chip->cores_num; j++) + sdev->dsp_core_ref_count[j] = 0; + /* disable ppcap interrupt */ hda_dsp_ctrl_ppcap_enable(sdev, false); hda_dsp_ctrl_ppcap_int_enable(sdev, false); @@ -962,3 +966,47 @@ void hda_dsp_d0i3_work(struct work_struct *work) "error: failed to set DSP state %d substate %d\n", target_state.state, target_state.substate); } + +int hda_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask | BIT(core), + }; + int ret, ret1; + + /* power up core */ + ret = hda_dsp_enable_core(sdev, BIT(core)); + if (ret < 0) { + dev_err(sdev->dev, "failed to power up core %d with err: %d\n", + core, ret); + return ret; + } + + /* No need to send IPC for primary core or if FW boot is not complete */ + if (sdev->fw_state != SOF_FW_BOOT_COMPLETE || core == SOF_DSP_PRIMARY_CORE) + return 0; + + /* Now notify DSP for secondary cores */ + ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); + if (ret < 0) { + dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n", + core, ret); + goto power_down; + } + + return ret; + +power_down: + /* power down core if it is host managed and return the original error if this fails too */ + ret1 = hda_dsp_core_reset_power_down(sdev, BIT(core)); + if (ret1 < 0) + dev_err(sdev->dev, "failed to power down core: %d with err: %d\n", core, ret1); + + return ret; +} diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 11f20a5a62df..f0cf8019d72d 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -70,7 +70,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) struct snd_sof_ipc_msg *msg = sdev->msg; struct sof_ipc_reply reply; struct sof_ipc_cmd_hdr *hdr; - int ret = 0; /* * Sometimes, there is unexpected reply ipc arriving. The reply @@ -94,35 +93,11 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) reply.hdr.cmd = SOF_IPC_GLB_REPLY; reply.hdr.size = sizeof(reply); memcpy(msg->reply_data, &reply, sizeof(reply)); - goto out; - } - - /* get IPC reply from DSP in the mailbox */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, - sizeof(reply)); - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; + msg->reply_error = 0; } else { - /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size && - /* getter payload is never known upfront */ - ((reply.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_PROBE)) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); + snd_sof_ipc_get_reply(sdev); } - -out: - msg->reply_error = ret; - } /* IPC handler thread */ @@ -198,8 +173,23 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) /* handle messages from DSP */ if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - /* this is a PANIC message !! */ - snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + bool non_recoverable = true; + + /* + * This is a PANIC message! + * + * If it is arriving during firmware boot and it is not + * the last boot attempt then change the non_recoverable + * to false as the DSP might be able to boot in the next + * iteration(s) + */ + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS && + hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS) + non_recoverable = false; + + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext), + non_recoverable); } else { /* normal message - process normally */ snd_sof_ipc_msgs_rx(sdev); diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index abad6d0ceb83..33306d2023a7 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -23,7 +23,6 @@ #include "../ops.h" #include "hda.h" -#define HDA_FW_BOOT_ATTEMPTS 3 #define HDA_CL_STREAM_FORMAT 0x40 static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, @@ -88,12 +87,14 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; unsigned int status; - u32 flags; + unsigned long mask; + char *dump_msg; + u32 flags, j; int ret; int i; /* step 1: power up corex */ - ret = snd_sof_dsp_core_power_up(sdev, chip->host_managed_cores_mask); + ret = hda_dsp_enable_core(sdev, chip->host_managed_cores_mask); if (ret < 0) { if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); @@ -148,8 +149,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) chip->ipc_ack_mask); /* step 5: power down cores that are no longer needed */ - ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask & - ~(chip->init_core_mask)); + ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask & + ~(chip->init_core_mask)); if (ret < 0) { if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, @@ -168,8 +169,14 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) HDA_DSP_REG_POLL_INTERVAL_US, chip->rom_init_timeout * USEC_PER_MSEC); - if (!ret) + if (!ret) { + /* set enabled cores mask and increment ref count for cores in init_core_mask */ + sdev->enabled_cores_mask |= chip->init_core_mask; + mask = sdev->enabled_cores_mask; + for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES) + sdev->dsp_core_ref_count[j]++; return 0; + } if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, @@ -183,9 +190,12 @@ err: if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) flags &= ~SOF_DBG_DUMP_OPTIONAL; - snd_sof_dsp_dbg_dump(sdev, flags); - snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask); + dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d", + hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS); + snd_sof_dsp_dbg_dump(sdev, dump_msg, flags); + hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); + kfree(dump_msg); return ret; } @@ -407,16 +417,19 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) hda_sdw_process_wakeen(sdev); /* - * at this point DSP ROM has been initialized and - * should be ready for code loading and firmware boot + * Set the boot_iteration to the last attempt, indicating that the + * DSP ROM has been initialized and from this point there will be no + * retry done to boot. + * + * Continue with code loading and firmware boot */ + hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS; ret = cl_copy_fw(sdev, stream); - if (!ret) { + if (!ret) dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); - } else { - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX); - dev_err(sdev->dev, "error: load fw failed ret: %d\n", ret); - } + else + snd_sof_dsp_dbg_dump(sdev, "Firmware download failed", + SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX); cleanup: /* @@ -474,46 +487,6 @@ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) return hda_dsp_ctrl_clock_power_gating(sdev, true); } -/* - * post fw run operations for ICL, - * Core 3 will be powered up and in stall when HPRO is enabled - */ -int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev) -{ - struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - int ret; - - if (sdev->first_boot) { - ret = hda_sdw_startup(sdev); - if (ret < 0) { - dev_err(sdev->dev, - "error: could not startup SoundWire links\n"); - return ret; - } - } - - hda_sdw_int_enable(sdev, true); - - /* - * The recommended HW programming sequence for ICL is to - * power up core 3 and keep it in stall if HPRO is enabled. - * Major difference between ICL and TGL, on ICL core 3 is managed by - * the host whereas on TGL it is handled by the firmware. - */ - if (!hda->clk_config_lpro) { - ret = snd_sof_dsp_core_power_up(sdev, BIT(3)); - if (ret < 0) { - dev_err(sdev->dev, "error: dsp core power up failed on core 3\n"); - return ret; - } - - snd_sof_dsp_stall(sdev, BIT(3)); - } - - /* re-enable clock gating and power gating */ - return hda_dsp_ctrl_clock_power_gating(sdev, true); -} - int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, const struct sof_ext_man_elem_header *hdr) { @@ -551,24 +524,3 @@ int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, return 0; } - -int hda_dsp_core_stall_icl(struct snd_sof_dev *sdev, unsigned int core_mask) -{ - struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - const struct sof_intel_dsp_desc *chip = hda->desc; - - /* make sure core_mask in host managed cores */ - core_mask &= chip->host_managed_cores_mask; - if (!core_mask) { - dev_err(sdev->dev, "error: core_mask is not in host managed cores\n"); - return -EINVAL; - } - - /* stall core */ - snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, - HDA_DSP_REG_ADSPCS, - HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), - HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); - - return 0; -} diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index cc8ddef37f37..d78aa5d8552d 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -32,6 +32,10 @@ static bool hda_always_enable_dmi_l1; module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444); MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1"); +static bool hda_disable_rewinds = IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DISABLE_REWINDS); +module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444); +MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds"); + u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate) { switch (rate) { @@ -120,8 +124,11 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, return ret; } - /* disable SPIB, to enable buffer wrap for stream */ - hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + /* enable SPIB when rewinds are disabled */ + if (hda_disable_rewinds) + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, 0); + else + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); /* update no_stream_position flag for ipc params */ if (hda && hda->no_ipc_position) { @@ -140,6 +147,29 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, return 0; } +/* update SPIB register with appl position */ +int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + struct snd_pcm_runtime *runtime = substream->runtime; + ssize_t appl_pos, buf_size; + u32 spib; + + appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr); + buf_size = frames_to_bytes(runtime, runtime->buffer_size); + + spib = appl_pos % buf_size; + + /* Allowable value for SPIB is 1 byte to max buffer size */ + if (!spib) + spib = buf_size; + + sof_io_write(sdev, hext_stream->spib_addr, spib); + + return 0; +} + int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd) { @@ -172,38 +202,74 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, goto found; } - /* - * DPIB/posbuf position mode: - * For Playback, Use DPIB register from HDA space which - * reflects the actual data transferred. - * For Capture, Use the position buffer for pointer, as DPIB - * is not accurate enough, its update may be completed - * earlier than the data written to DDR. - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (sof_hda_position_quirk) { + case SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY: + /* + * This legacy code, inherited from the Skylake driver, + * mixes DPIB registers and DPIB DDR updates and + * does not seem to follow any known hardware recommendations. + * It's not clear e.g. why there is a different flow + * for capture and playback, the only information that matters is + * what traffic class is used, and on all SOF-enabled platforms + * only VC0 is supported so the work-around was likely not necessary + * and quite possibly wrong. + */ + + /* DPIB/posbuf position mode: + * For Playback, Use DPIB register from HDA space which + * reflects the actual data transferred. + * For Capture, Use the position buffer for pointer, as DPIB + * is not accurate enough, its update may be completed + * earlier than the data written to DDR. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * + hstream->index)); + } else { + /* + * For capture stream, we need more workaround to fix the + * position incorrect issue: + * + * 1. Wait at least 20us before reading position buffer after + * the interrupt generated(IOC), to make sure position update + * happens on frame boundary i.e. 20.833uSec for 48KHz. + * 2. Perform a dummy Read to DPIB register to flush DMA + * position value. + * 3. Read the DMA Position from posbuf. Now the readback + * value should be >= period boundary. + */ + usleep_range(20, 21); + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * + hstream->index)); + pos = snd_hdac_stream_get_pos_posbuf(hstream); + } + break; + case SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS: + /* + * In case VC1 traffic is disabled this is the recommended option + */ pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, AZX_REG_VS_SDXDPIB_XBASE + (AZX_REG_VS_SDXDPIB_XINTERVAL * hstream->index)); - } else { + break; + case SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE: /* - * For capture stream, we need more workaround to fix the - * position incorrect issue: - * - * 1. Wait at least 20us before reading position buffer after - * the interrupt generated(IOC), to make sure position update - * happens on frame boundary i.e. 20.833uSec for 48KHz. - * 2. Perform a dummy Read to DPIB register to flush DMA - * position value. - * 3. Read the DMA Position from posbuf. Now the readback - * value should be >= period boundary. + * This is the recommended option when VC1 is enabled. + * While this isn't needed for SOF platforms it's added for + * consistency and debug. */ - usleep_range(20, 21); - snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - AZX_REG_VS_SDXDPIB_XBASE + - (AZX_REG_VS_SDXDPIB_XINTERVAL * - hstream->index)); pos = snd_hdac_stream_get_pos_posbuf(hstream); + break; + default: + dev_err_once(sdev->dev, "hda_position_quirk value %d not supported\n", + sof_hda_position_quirk); + pos = 0; + break; } if (pos >= hstream->bufsize) @@ -235,6 +301,13 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, } /* + * if we want the .ack to work, we need to prevent the control from being mapped. + * The status can still be mapped. + */ + if (hda_disable_rewinds) + runtime->hw.info |= SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR; + + /* * All playback streams are DMI L1 capable, capture streams need * pause push/release to be disabled */ diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 1d845c2cbc33..ba60807fbd8f 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -279,6 +279,45 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) return 0; } +static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream) +{ + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + int timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + u32 val; + + /* enter stream reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, + SOF_STREAM_SD_OFFSET_CRST); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); + if (val & SOF_STREAM_SD_OFFSET_CRST) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "timeout waiting for stream reset\n"); + return -ETIMEDOUT; + } + + timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + + /* exit stream reset and wait to read a zero before reading any other register */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 0x0); + + /* wait for hardware to report that stream is out of reset */ + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); + if ((val & SOF_STREAM_SD_OFFSET_CRST) == 0) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "timeout waiting for stream to exit reset\n"); + return -ETIMEDOUT; + } + + return 0; +} + int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, int cmd) { @@ -290,7 +329,6 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, /* cmd must be for audio stream */ switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_START: snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, @@ -433,12 +471,13 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params) { + const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + int ret; u32 dma_start = SOF_HDA_SD_CTL_DMA_START; - u32 val, mask; + u32 mask; u32 run; if (!stream) { @@ -483,36 +522,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, SOF_HDA_CL_DMA_SD_INT_MASK); /* stream reset */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, - 0x1); - udelay(3); - do { - val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - sd_offset); - if (val & 0x1) - break; - } while (--timeout); - if (timeout == 0) { - dev_err(sdev->dev, "error: stream reset failed\n"); - return -ETIMEDOUT; - } - - timeout = HDA_DSP_STREAM_RESET_TIMEOUT; - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, - 0x0); - - /* wait for hardware to report that stream is out of reset */ - udelay(3); - do { - val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - sd_offset); - if ((val & 0x1) == 0) - break; - } while (--timeout); - if (timeout == 0) { - dev_err(sdev->dev, "error: timeout waiting for stream reset\n"); - return -ETIMEDOUT; - } + ret = hda_dsp_stream_reset(sdev, hstream); + if (ret < 0) + return ret; if (hstream->posbuf) *hstream->posbuf = 0; @@ -572,6 +584,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, /* * Recommended hardware programming sequence for HDAudio DMA format + * on earlier platforms - this is not needed on newer platforms * * 1. Put DMA into coupled mode by clearing PPCTL.PROCEN bit * for corresponding stream index before the time of writing @@ -581,9 +594,11 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, * enable decoupled mode */ - /* couple host and link DMA, disable DSP features */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - mask, 0); + if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + /* couple host and link DMA, disable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, 0); + } /* program stream format */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -591,9 +606,11 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, SOF_HDA_ADSP_REG_CL_SD_FORMAT, 0xffff, hstream->format_val); - /* decouple host and link DMA, enable DSP features */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - mask, mask); + if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + /* decouple host and link DMA, enable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask); + } /* program last valid index */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -608,9 +625,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, upper_32_bits(hstream->bdl.addr)); - /* enable position buffer */ - if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) - & SOF_HDA_ADSP_DPLBASE_ENABLE)) { + /* enable position buffer, if needed */ + if (bus->use_posbuf && bus->posbuf.addr && + !(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) + & SOF_HDA_ADSP_DPLBASE_ENABLE)) { snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE, upper_32_bits(bus->posbuf.addr)); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, @@ -647,6 +665,11 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, hstream); struct hdac_bus *bus = sof_to_bus(sdev); u32 mask = 0x1 << stream->index; + int ret; + + ret = hda_dsp_stream_reset(sdev, stream); + if (ret < 0) + return ret; spin_lock_irq(&bus->reg_lock); /* couple host and link DMA if link DMA channel is idle */ @@ -655,6 +678,8 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, SOF_HDA_REG_PP_PPCTL, mask, 0); spin_unlock_irq(&bus->reg_lock); + hda_dsp_stream_spib_config(sdev, link_dev, HDA_DSP_SPIB_DISABLE, 0); + stream->substream = NULL; return 0; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 2c0d4d06ab36..c8fb082209ce 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -41,7 +41,7 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 -int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; @@ -60,7 +60,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) /* DAI already configured, reset it before reconfiguring it */ if (sof_dai->configured) { - ret = hda_ctrl_dai_widget_free(w); + ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); if (ret < 0) return ret; } @@ -78,8 +78,10 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) return ret; } - /* set HW_PARAMS flag */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_PARAMS); + /* set HW_PARAMS flag along with quirks */ + config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; + /* send DAI_CONFIG IPC */ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, @@ -94,7 +96,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) return 0; } -int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w) +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; @@ -117,8 +119,9 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w) config = &sof_dai->dai_config[sof_dai->current_config]; - /* set HW_FREE flag */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_FREE); + /* set HW_FREE flag along with any quirks */ + config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, &reply, sizeof(reply)); @@ -134,17 +137,6 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w) return sof_widget_free(sdev, swidget); } -static const struct sof_intel_dsp_desc - *get_chip_info(struct snd_sof_pdata *pdata) -{ - const struct sof_dev_desc *desc = pdata->desc; - const struct sof_intel_dsp_desc *chip_info; - - chip_info = desc->chip_info; - - return chip_info; -} - #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) /* @@ -184,23 +176,19 @@ static int sdw_dai_config_ipc(struct snd_sof_dev *sdev, config->alh.stream_id = alh_stream_id; if (setup) - return hda_ctrl_dai_widget_setup(w); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); - return hda_ctrl_dai_widget_free(w); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int sdw_params_stream(struct device *dev, struct sdw_intel_stream_params_data *params_data) { - struct snd_pcm_substream *substream = params_data->substream; struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = params_data->dai; struct snd_soc_dapm_widget *w; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = d->playback_widget; - else - w = d->capture_widget; + w = snd_soc_dai_get_widget(d, params_data->stream); return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id, d->id, true); @@ -209,15 +197,11 @@ static int sdw_params_stream(struct device *dev, static int sdw_free_stream(struct device *dev, struct sdw_intel_stream_free_data *free_data) { - struct snd_pcm_substream *substream = free_data->substream; struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = free_data->dai; struct snd_soc_dapm_widget *w; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = d->playback_widget; - else - w = d->capture_widget; + w = snd_soc_dai_get_widget(d, free_data->stream); /* send invalid stream_id */ return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false); @@ -440,6 +424,10 @@ MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode"); #define hda_use_msi (1) #endif +int sof_hda_position_quirk = SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS; +module_param_named(position_quirk, sof_hda_position_quirk, int, 0444); +MODULE_PARM_DESC(position_quirk, "SOF HDaudio position quirk"); + static char *hda_model; module_param(hda_model, charp, 0444); MODULE_PARM_DESC(hda_model, "Use the given HDA board model."); @@ -478,7 +466,7 @@ static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = { {HDA_DSP_ROM_NULL_FW_ENTRY, "error: null FW entry point"}, }; -static void hda_dsp_get_status(struct snd_sof_dev *sdev) +static void hda_dsp_get_status(struct snd_sof_dev *sdev, const char *level) { u32 status; int i; @@ -488,8 +476,8 @@ static void hda_dsp_get_status(struct snd_sof_dev *sdev) for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) { if (status == hda_dsp_rom_msg[i].code) { - dev_err(sdev->dev, "%s - code %8.8x\n", - hda_dsp_rom_msg[i].msg, status); + dev_printk(level, sdev->dev, "%s - code %8.8x\n", + hda_dsp_rom_msg[i].msg, status); return; } } @@ -527,7 +515,8 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev, } /* dump the first 8 dwords representing the extended ROM status */ -static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, u32 flags) +static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *level, + u32 flags) { char msg[128]; int len = 0; @@ -539,18 +528,19 @@ static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, u32 flags) len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value); } - dev_err(sdev->dev, "extended rom status: %s", msg); + dev_printk(level, sdev->dev, "extended rom status: %s", msg); } void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { + char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; struct sof_ipc_dsp_oops_xtensa xoops; struct sof_ipc_panic_info panic_info; u32 stack[HDA_DSP_STACK_DUMP_SIZE]; /* print ROM/FW status */ - hda_dsp_get_status(sdev); + hda_dsp_get_status(sdev, level); if (flags & SOF_DBG_DUMP_REGS) { u32 status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_STATUS); @@ -558,10 +548,10 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) hda_dsp_get_registers(sdev, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, - stack, HDA_DSP_STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, level, status, panic, &xoops, + &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); } else { - hda_dsp_dump_ext_rom_status(sdev, flags); + hda_dsp_dump_ext_rom_status(sdev, level, flags); } } @@ -618,7 +608,10 @@ static int hda_init(struct snd_sof_dev *sdev) /* HDA bus init */ sof_hda_bus_init(bus, &pci->dev); - bus->use_posbuf = 1; + if (sof_hda_position_quirk == SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS) + bus->use_posbuf = 0; + else + bus->use_posbuf = 1; bus->bdl_pos_adj = 0; bus->sync_write = 1; @@ -915,6 +908,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) goto err; } + sdev->num_cores = chip->cores_num; + hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL); if (!hdev) return -ENOMEM; @@ -1050,9 +1045,9 @@ err: int hda_dsp_remove(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; struct hdac_bus *bus = sof_to_bus(sdev); struct pci_dev *pci = to_pci_dev(sdev->dev); - const struct sof_intel_dsp_desc *chip = hda->desc; /* cancel any attempt for DSP D0I3 */ cancel_delayed_work_sync(&hda->d0i3_work); @@ -1077,7 +1072,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) /* disable cores */ if (chip) - snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask); + hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); /* disable DSP */ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, @@ -1104,7 +1099,8 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) } #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) -static int hda_generic_machine_select(struct snd_sof_dev *sdev) +static void hda_generic_machine_select(struct snd_sof_dev *sdev, + struct snd_soc_acpi_mach **mach) { struct hdac_bus *bus = sof_to_bus(sdev); struct snd_soc_acpi_mach_params *mach_params; @@ -1136,7 +1132,7 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) * - one HDMI codec, and/or * - one external HDAudio codec */ - if (!pdata->machine && codec_num <= 2) { + if (!*mach && codec_num <= 2) { hda_mach = snd_soc_acpi_intel_hda_machines; dev_info(bus->dev, "using HDA machine driver %s now\n", @@ -1151,10 +1147,9 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) tplg_filename = hda_mach->sof_tplg_filename; ret = dmic_topology_fixup(sdev, &tplg_filename, idisp_str, &dmic_num); if (ret < 0) - return ret; + return; hda_mach->mach_params.dmic_num = dmic_num; - pdata->machine = hda_mach; pdata->tplg_filename = tplg_filename; if (codec_num == 2) { @@ -1164,23 +1159,22 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) */ hda_mach->mach_params.link_mask = 0; } + + *mach = hda_mach; } } /* used by hda machine driver to create dai links */ - if (pdata->machine) { - mach_params = (struct snd_soc_acpi_mach_params *) - &pdata->machine->mach_params; + if (*mach) { + mach_params = &(*mach)->mach_params; mach_params->codec_mask = bus->codec_mask; mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi; } - - return 0; } #else -static int hda_generic_machine_select(struct snd_sof_dev *sdev) +static void hda_generic_machine_select(struct snd_sof_dev *sdev, + struct snd_soc_acpi_mach **mach) { - return 0; } #endif @@ -1263,7 +1257,7 @@ static bool link_slaves_found(struct snd_sof_dev *sdev, return true; } -static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct snd_soc_acpi_link_adr *link; @@ -1281,7 +1275,7 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) * machines, for mixed cases with I2C/I2S the detection relies * on the HID list. */ - if (link_mask && !pdata->machine) { + if (link_mask) { for (mach = pdata->desc->alt_machines; mach && mach->link_mask; mach++) { /* @@ -1316,7 +1310,6 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) if (mach && mach->link_mask) { int dmic_num = 0; - pdata->machine = mach; mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; mach->mach_params.platform = dev_name(sdev->dev); @@ -1338,9 +1331,8 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) int ret; ret = dmic_topology_fixup(sdev, &tplg_filename, "", &dmic_num); - if (ret < 0) - return ret; + return NULL; pdata->tplg_filename = tplg_filename; } @@ -1350,35 +1342,36 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) "SoundWire machine driver %s topology %s\n", mach->drv_name, pdata->tplg_filename); - } else { - dev_info(sdev->dev, - "No SoundWire machine driver found\n"); + + return mach; } + + dev_info(sdev->dev, "No SoundWire machine driver found\n"); } - return 0; + return NULL; } #else -static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { - return 0; + return NULL; } #endif -void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, +void hda_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; } -void hda_machine_select(struct snd_sof_dev *sdev) +struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -1393,8 +1386,6 @@ void hda_machine_select(struct snd_sof_dev *sdev) if (!sof_pdata->tplg_filename) sof_pdata->tplg_filename = mach->sof_tplg_filename; - sof_pdata->machine = mach; - if (mach->link_mask) { mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; @@ -1404,16 +1395,18 @@ void hda_machine_select(struct snd_sof_dev *sdev) /* * If I2S fails, try SoundWire */ - hda_sdw_machine_select(sdev); + if (!mach) + mach = hda_sdw_machine_select(sdev); /* * Choose HDA generic machine driver if mach is NULL. * Otherwise, set certain mach params. */ - hda_generic_machine_select(sdev); - - if (!sof_pdata->machine) + hda_generic_machine_select(sdev, &mach); + if (!mach) dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); + + return mach; } int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 1195018a1f4f..03a6bb7a165c 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -273,7 +273,7 @@ #define BXT_D0I3_DELAY 5000 #define FW_CL_STREAM_NUMBER 0x1 -#define HDA_FW_BOOT_ATTEMPTS 3 +#define HDA_FW_BOOT_ATTEMPTS 3 /* ADSPCS - Audio DSP Control & Status */ @@ -487,6 +487,8 @@ struct sof_intel_hda_stream { (SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \ + SOF_HDA_ADSP_LOADER_BASE) +#define SOF_STREAM_SD_OFFSET_CRST 0x1 + /* * DSP Core services. */ @@ -496,6 +498,7 @@ int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_get(struct snd_sof_dev *sdev, int core); void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev); void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev); @@ -533,6 +536,7 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); +int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* * DSP Stream Operations. @@ -614,8 +618,6 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev); /* pre and post fw run ops */ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev); int hda_dsp_post_fw_run(struct snd_sof_dev *sdev); -int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev); -int hda_dsp_core_stall_icl(struct snd_sof_dev *sdev, unsigned int core_mask); /* parse platform specific ext manifest ops */ int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, @@ -726,8 +728,8 @@ extern const struct sof_intel_dsp_desc jsl_chip_info; extern const struct sof_intel_dsp_desc adls_chip_info; /* machine driver select */ -void hda_machine_select(struct snd_sof_dev *sdev); -void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, +struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev); +void hda_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* PCI driver selection and probe */ @@ -735,7 +737,13 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) struct snd_sof_dai; struct sof_ipc_dai_config; -int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w); -int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w); +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); + +#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */ +#define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */ +#define SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE (2) /* recommended with VC0 or VC1 */ + +extern int sof_hda_position_quirk; #endif diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 0b2cc331d55b..f75e3983969f 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -18,12 +18,75 @@ #include "hda-ipc.h" #include "../sof-audio.h" +#define ICL_DSP_HPRO_CORE_ID 3 + static const struct snd_sof_debugfs_map icl_dsp_debugfs[] = { {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS}, {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, }; +static int icl_dsp_core_stall(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + + /* make sure core_mask in host managed cores */ + core_mask &= chip->host_managed_cores_mask; + if (!core_mask) { + dev_err(sdev->dev, "error: core_mask is not in host managed cores\n"); + return -EINVAL; + } + + /* stall core */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); + + return 0; +} + +/* + * post fw run operation for ICL. + * Core 3 will be powered up and in stall when HPRO is enabled + */ +static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + int ret; + + if (sdev->first_boot) { + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: could not startup SoundWire links\n"); + return ret; + } + } + + hda_sdw_int_enable(sdev, true); + + /* + * The recommended HW programming sequence for ICL is to + * power up core 3 and keep it in stall if HPRO is enabled. + */ + if (!hda->clk_config_lpro) { + ret = hda_dsp_enable_core(sdev, BIT(ICL_DSP_HPRO_CORE_ID)); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core power up failed on core %d\n", + ICL_DSP_HPRO_CORE_ID); + return ret; + } + + sdev->enabled_cores_mask |= BIT(ICL_DSP_HPRO_CORE_ID); + sdev->dsp_core_ref_count[ICL_DSP_HPRO_CORE_ID]++; + + snd_sof_dsp_stall(sdev, BIT(ICL_DSP_HPRO_CORE_ID)); + } + + /* re-enable clock gating and power gating */ + return hda_dsp_ctrl_clock_power_gating(sdev, true); +} + /* Icelake ops */ const struct snd_sof_dsp_ops sof_icl_ops = { /* probe/remove/shutdown */ @@ -77,6 +140,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ @@ -92,18 +156,17 @@ const struct snd_sof_dsp_ops sof_icl_ops = { /* pre/post fw run */ .pre_fw_run = hda_dsp_pre_fw_run, - .post_fw_run = hda_dsp_post_fw_run_icl, + .post_fw_run = icl_dsp_post_fw_run, /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ + .core_get = hda_dsp_core_get, /* firmware run */ .run = hda_dsp_cl_boot_firmware_iccmax, - .stall = hda_dsp_core_stall_icl, + .stall = icl_dsp_core_stall, /* trace callback */ .trace_init = hda_dsp_trace_init, diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index 18eb41b8a8f4..f8c841caa362 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -55,9 +55,18 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev) struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct pci_dev *pci = to_pci_dev(sdev->dev); + const struct sof_intel_dsp_desc *chip; u32 base, size; int ret; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "error: no such device supported\n"); + return -EIO; + } + + sdev->num_cores = chip->cores_num; + /* DSP DMA can only access low 31 bits of host memory */ ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31)); if (ret < 0) { diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index e9f7d4d7fcce..f36cd9d5eb94 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -151,6 +151,9 @@ #define PCI_PMCS 0x84 #define PCI_PMCS_PS_MASK 0x3 +/* Intel quirks */ +#define SOF_INTEL_PROCEN_FMT_QUIRK BIT(0) + /* DSP hardware descriptor */ struct sof_intel_dsp_desc { int cores_num; @@ -166,6 +169,7 @@ struct sof_intel_dsp_desc { int ssp_base_offset; /* base address of the SSPs */ u32 sdw_shim_base; u32 sdw_alh_base; + u32 quirks; bool (*check_sdw_irq)(struct snd_sof_dev *sdev); }; @@ -177,4 +181,11 @@ struct sof_intel_stream { size_t posn_offset; }; +static inline const struct sof_intel_dsp_desc *get_chip_info(struct snd_sof_pdata *pdata) +{ + const struct sof_dev_desc *desc = pdata->desc; + + return desc->chip_info; +} + #endif diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 48da8e7a67bc..7f7929c5cb88 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -20,6 +20,46 @@ static const struct snd_sof_debugfs_map tgl_dsp_debugfs[] = { {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, }; +static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask | BIT(core), + }; + + /* power up primary core if not already powered up and return */ + if (core == SOF_DSP_PRIMARY_CORE) + return hda_dsp_enable_core(sdev, BIT(core)); + + /* notify DSP for secondary cores */ + return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); +} + +static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask & ~BIT(core), + }; + + /* power down primary core and return */ + if (core == SOF_DSP_PRIMARY_CORE) + return hda_dsp_core_reset_power_down(sdev, BIT(core)); + + /* notify DSP for secondary cores */ + return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); +} + /* Tigerlake ops */ const struct snd_sof_dsp_ops sof_tgl_ops = { /* probe/remove/shutdown */ @@ -73,6 +113,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ @@ -93,9 +134,9 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ + .core_get = tgl_dsp_core_get, + .core_put = tgl_dsp_core_put, /* firmware run */ .run = hda_dsp_cl_boot_firmware_iccmax, diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index e6c53c6c470e..5bcf906d90af 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -173,7 +173,22 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) } break; case SOF_IPC_GLB_TRACE_MSG: - str = "GLB_TRACE_MSG"; break; + str = "GLB_TRACE_MSG"; + switch (type) { + case SOF_IPC_TRACE_DMA_PARAMS: + str2 = "DMA_PARAMS"; break; + case SOF_IPC_TRACE_DMA_POSITION: + str2 = "DMA_POSITION"; break; + case SOF_IPC_TRACE_DMA_PARAMS_EXT: + str2 = "DMA_PARAMS_EXT"; break; + case SOF_IPC_TRACE_FILTER_UPDATE: + str2 = "FILTER_UPDATE"; break; + case SOF_IPC_TRACE_DMA_FREE: + str2 = "DMA_FREE"; break; + default: + str2 = "unknown type"; break; + } + break; case SOF_IPC_GLB_TEST_MSG: str = "GLB_TEST_MSG"; switch (type) { @@ -287,7 +302,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, struct snd_sof_ipc_msg *msg; int ret; - if (ipc->disable_ipc_tx) + if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE) return -ENODEV; /* @@ -379,6 +394,67 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, } EXPORT_SYMBOL(sof_ipc_tx_message_no_pm); +/* Generic helper function to retrieve the reply */ +void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc_reply reply; + int ret = 0; + + /* + * Sometimes, there is unexpected reply ipc arriving. The reply + * ipc belongs to none of the ipcs sent from driver. + * In this case, the driver must ignore the ipc. + */ + if (!msg) { + dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); + return; + } + + /* get the generic reply */ + snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, &reply, + sizeof(reply)); + + if (reply.error < 0) { + memcpy(msg->reply_data, &reply, sizeof(reply)); + ret = reply.error; + } else if (!reply.hdr.size) { + /* Reply should always be >= sizeof(struct sof_ipc_reply) */ + if (msg->reply_size) + dev_err(sdev->dev, + "empty reply received, expected %zu bytes\n", + msg->reply_size); + else + dev_err(sdev->dev, "empty reply received\n"); + + ret = -EINVAL; + } else if (msg->reply_size > 0) { + if (reply.hdr.size == msg->reply_size) { + ret = 0; + } else if (reply.hdr.size < msg->reply_size) { + dev_dbg(sdev->dev, + "reply size (%u) is less than expected (%zu)\n", + reply.hdr.size, msg->reply_size); + + msg->reply_size = reply.hdr.size; + ret = 0; + } else { + dev_err(sdev->dev, + "reply size (%u) exceeds the buffer size (%zu)\n", + reply.hdr.size, msg->reply_size); + ret = -EINVAL; + } + + /* get the full message if reply.hdr.size <= msg->reply_size */ + if (!ret) + snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, msg->reply_size); + } + + msg->reply_error = ret; +} +EXPORT_SYMBOL(snd_sof_ipc_get_reply); + /* handle reply message from DSP */ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) { @@ -460,7 +536,7 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) if (err < 0) sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); else - sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK); /* wake up firmware loader */ wake_up(&sdev->boot_wait); @@ -547,7 +623,8 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) if (spcm->pcm.compress) snd_sof_compr_fragment_elapsed(stream->cstream); - else if (!stream->substream->runtime->no_period_wakeup) + else if (stream->substream->runtime && + !stream->substream->runtime->no_period_wakeup) /* only inform ALSA for period_wakeup mode */ snd_sof_pcm_period_elapsed(stream->substream); } @@ -645,11 +722,6 @@ static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type, sparams->src = (u8 *)src->chanv; sparams->dst = (u8 *)dst->chanv; break; - case SOF_CTRL_TYPE_VALUE_COMP_GET: - case SOF_CTRL_TYPE_VALUE_COMP_SET: - sparams->src = (u8 *)src->compv; - sparams->dst = (u8 *)dst->compv; - break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: sparams->src = (u8 *)src->data->data; @@ -669,7 +741,7 @@ static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type, static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, struct sof_ipc_ctrl_data *cdata, struct sof_ipc_ctrl_data_params *sparams, - bool send) + bool set) { struct sof_ipc_ctrl_data *partdata; size_t send_bytes; @@ -684,7 +756,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, if (!partdata) return -ENOMEM; - if (send) + if (set) err = sof_get_ctrl_copy_params(cdata->type, cdata, partdata, sparams); else @@ -713,7 +785,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, msg_bytes -= send_bytes; partdata->elems_remaining = msg_bytes; - if (send) + if (set) memcpy(sparams->dst, sparams->src + offset, send_bytes); err = sof_ipc_tx_message_unlocked(sdev->ipc, @@ -725,7 +797,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, if (err < 0) break; - if (!send) + if (!set) memcpy(sparams->dst + offset, sparams->src, send_bytes); offset += pl_size; @@ -740,11 +812,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, /* * IPC get()/set() for kcontrols. */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - u32 ipc_cmd, - enum sof_ipc_ctrl_type ctrl_type, - enum sof_ipc_ctrl_cmd ctrl_cmd, - bool send) +int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set) { struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; @@ -752,9 +820,11 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; struct sof_ipc_ctrl_data_params sparams; + enum sof_ipc_ctrl_type ctrl_type; struct snd_sof_widget *swidget; bool widget_found = false; size_t send_bytes; + u32 ipc_cmd; int err; list_for_each_entry(swidget, &sdev->widget_list, list) { @@ -782,7 +852,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, /* write/read value header via mmaped region */ send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) * cdata->num_elems; - if (send) + if (set) err = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, scontrol->readback_offset, cdata->chanv, send_bytes); @@ -794,12 +864,25 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, if (err) dev_err_once(sdev->dev, "error: %s TYPE_IRAM failed\n", - send ? "write to" : "read from"); + set ? "write to" : "read from"); return err; } + /* + * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the + * direction + * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently + * for ctrl_type + */ + if (cdata->cmd == SOF_CTRL_CMD_BINARY) { + ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA; + ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET; + } else { + ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE; + ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET; + } + cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; - cdata->cmd = ctrl_cmd; cdata->type = ctrl_type; cdata->comp_id = scontrol->comp_id; cdata->msg_index = 0; @@ -813,13 +896,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data); sparams.elems = scontrol->num_channels; break; - case SOF_CTRL_TYPE_VALUE_COMP_GET: - case SOF_CTRL_TYPE_VALUE_COMP_SET: - sparams.msg_bytes = scontrol->num_channels * - sizeof(struct sof_ipc_ctrl_value_comp); - sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data); - sparams.elems = scontrol->num_channels; - break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: sparams.msg_bytes = cdata->data->size; @@ -858,7 +934,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, return -EINVAL; } - err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, send); + err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, set); if (err < 0) dev_err(sdev->dev, "error: set/get large ctrl ipc comp %d\n", diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index c04646647637..697f03565a70 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -820,8 +820,8 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) /* boot the firmware on the DSP */ ret = snd_sof_dsp_run(sdev); if (ret < 0) { - dev_err(sdev->dev, "error: failed to start DSP\n"); - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); + snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP", + SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); return ret; } @@ -835,16 +835,13 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, msecs_to_jiffies(sdev->boot_timeout)); if (ret == 0) { - dev_err(sdev->dev, "error: firmware boot failure\n"); - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | + snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); - sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return -EIO; } - if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) - dev_dbg(sdev->dev, "firmware boot complete\n"); - else + if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) return -EIO; /* FW boots but fw_ready op failed */ /* perform post fw run operations */ @@ -854,6 +851,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return ret; } + dev_dbg(sdev->dev, "firmware boot complete\n"); + sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + return 0; } EXPORT_SYMBOL(snd_sof_run_firmware); diff --git a/sound/soc/sof/mediatek/Kconfig b/sound/soc/sof/mediatek/Kconfig new file mode 100644 index 000000000000..aeacf0e5bfbb --- /dev/null +++ b/sound/soc/sof/mediatek/Kconfig @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +config SND_SOC_SOF_MTK_TOPLEVEL + bool "SOF support for MTK audio DSPs" + depends on ARM64 || COMPILE_TEST + depends on SND_SOC_SOF_OF + help + This adds support for Sound Open Firmware for Mediatek platforms. + It is top level for all mediatek platforms. + Say Y if you have such a device. + If unsure select "N". + +if SND_SOC_SOF_MTK_TOPLEVEL +config SND_SOC_SOF_MTK_COMMON + tristate + select SND_SOC_SOF_OF_DEV + select SND_SOC_SOF + select SND_SOC_SOF_XTENSA + select SND_SOC_SOF_COMPRESS + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + +config SND_SOC_SOF_MT8195 + tristate "SOF support for MT8195 audio DSP" + select SND_SOC_SOF_MTK_COMMON + help + This adds support for Sound Open Firmware for Mediatek platforms + using the mt8195 processors. + Say Y if you have such a device. + If unsure select "N". + +endif ## SND_SOC_SOF_MTK_TOPLEVEL diff --git a/sound/soc/sof/mediatek/Makefile b/sound/soc/sof/mediatek/Makefile new file mode 100644 index 000000000000..e8ec6da981de --- /dev/null +++ b/sound/soc/sof/mediatek/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +obj-$(CONFIG_SND_SOC_SOF_MT8195) += mt8195/ diff --git a/sound/soc/sof/mediatek/adsp_helper.h b/sound/soc/sof/mediatek/adsp_helper.h new file mode 100644 index 000000000000..6734e2c0c6b1 --- /dev/null +++ b/sound/soc/sof/mediatek/adsp_helper.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2021 MediaTek Corporation. All rights reserved. + */ + +#ifndef __MTK_ADSP_HELPER_H__ +#define __MTK_ADSP_HELPER_H__ + +/* + * Global important adsp data structure. + */ +#define DSP_MBOX_NUM 3 + +struct mtk_adsp_chip_info { + phys_addr_t pa_sram; + phys_addr_t pa_dram; /* adsp dram physical base */ + phys_addr_t pa_shared_dram; /* adsp dram physical base */ + phys_addr_t pa_cfgreg; + phys_addr_t pa_mboxreg[DSP_MBOX_NUM]; + u32 sramsize; + u32 dramsize; + u32 cfgregsize; + void __iomem *va_sram; /* corresponding to pa_sram */ + void __iomem *va_dram; /* corresponding to pa_dram */ + void __iomem *va_cfgreg; + void __iomem *va_mboxreg[DSP_MBOX_NUM]; + void __iomem *shared_sram; /* part of va_sram */ + void __iomem *shared_dram; /* part of va_dram */ + phys_addr_t adsp_bootup_addr; + int dram_offset; /*dram offset between system and dsp view*/ +}; + +struct adsp_priv { + struct device *dev; + struct snd_sof_dev *sdev; + + /* DSP IPC handler */ + struct mbox_controller *adsp_mbox; + + struct mtk_adsp_chip_info *adsp; + struct clk **clk; + u32 (*ap2adsp_addr)(u32 addr, void *data); + u32 (*adsp2ap_addr)(u32 addr, void *data); + + void *private_data; +}; + +#endif diff --git a/sound/soc/sof/mediatek/mt8195/Makefile b/sound/soc/sof/mediatek/mt8195/Makefile new file mode 100644 index 000000000000..afc4f21fccc5 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +snd-sof-mt8195-objs := mt8195.o mt8195-clk.o mt8195-loader.o +obj-$(CONFIG_SND_SOC_SOF_MT8195) += snd-sof-mt8195.o diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c new file mode 100644 index 000000000000..6bcb4b9b00fb --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2021 Mediatek Corporation. All rights reserved. +// +// Author: YC Hung <yc.hung@mediatek.com> +// +// Hardware interface for mt8195 DSP clock + +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/io.h> +#include "mt8195.h" +#include "mt8195-clk.h" +#include "../adsp_helper.h" +#include "../../sof-audio.h" + +static const char *adsp_clks[ADSP_CLK_MAX] = { + [CLK_TOP_ADSP] = "adsp_sel", + [CLK_TOP_CLK26M] = "clk26m_ck", + [CLK_TOP_AUDIO_LOCAL_BUS] = "audio_local_bus", + [CLK_TOP_MAINPLL_D7_D2] = "mainpll_d7_d2", + [CLK_SCP_ADSP_AUDIODSP] = "scp_adsp_audiodsp", + [CLK_TOP_AUDIO_H] = "audio_h", +}; + +int mt8195_adsp_init_clock(struct snd_sof_dev *sdev) +{ + struct device *dev = sdev->dev; + struct adsp_priv *priv = sdev->pdata->hw_pdata; + int i; + + priv->clk = devm_kcalloc(dev, ADSP_CLK_MAX, sizeof(*priv->clk), GFP_KERNEL); + + if (!priv->clk) + return -ENOMEM; + + for (i = 0; i < ADSP_CLK_MAX; i++) { + priv->clk[i] = devm_clk_get(dev, adsp_clks[i]); + if (IS_ERR(priv->clk[i])) + return PTR_ERR(priv->clk[i]); + } + + return 0; +} + +static int adsp_enable_all_clock(struct snd_sof_dev *sdev) +{ + struct device *dev = sdev->dev; + struct adsp_priv *priv = sdev->pdata->hw_pdata; + int ret; + + ret = clk_prepare_enable(priv->clk[CLK_TOP_MAINPLL_D7_D2]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(mainpll_d7_d2) fail %d\n", + __func__, ret); + return ret; + } + + ret = clk_prepare_enable(priv->clk[CLK_TOP_ADSP]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(adsp_sel) fail %d\n", + __func__, ret); + goto disable_mainpll_d7_d2_clk; + } + + ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(audio_local_bus) fail %d\n", + __func__, ret); + goto disable_dsp_sel_clk; + } + + ret = clk_prepare_enable(priv->clk[CLK_SCP_ADSP_AUDIODSP]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(scp_adsp_audiodsp) fail %d\n", + __func__, ret); + goto disable_audio_local_bus_clk; + } + + ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIO_H]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(audio_h) fail %d\n", + __func__, ret); + goto disable_scp_adsp_audiodsp_clk; + } + + return 0; + +disable_scp_adsp_audiodsp_clk: + clk_disable_unprepare(priv->clk[CLK_SCP_ADSP_AUDIODSP]); +disable_audio_local_bus_clk: + clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]); +disable_dsp_sel_clk: + clk_disable_unprepare(priv->clk[CLK_TOP_ADSP]); +disable_mainpll_d7_d2_clk: + clk_disable_unprepare(priv->clk[CLK_TOP_MAINPLL_D7_D2]); + + return ret; +} + +static void adsp_disable_all_clock(struct snd_sof_dev *sdev) +{ + struct adsp_priv *priv = sdev->pdata->hw_pdata; + + clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_H]); + clk_disable_unprepare(priv->clk[CLK_SCP_ADSP_AUDIODSP]); + clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]); + clk_disable_unprepare(priv->clk[CLK_TOP_ADSP]); + clk_disable_unprepare(priv->clk[CLK_TOP_MAINPLL_D7_D2]); +} + +static int adsp_default_clk_init(struct snd_sof_dev *sdev, bool enable) +{ + struct device *dev = sdev->dev; + struct adsp_priv *priv = sdev->pdata->hw_pdata; + int ret; + + dev_dbg(dev, "%s: %s\n", __func__, enable ? "on" : "off"); + + if (enable) { + ret = clk_set_parent(priv->clk[CLK_TOP_ADSP], + priv->clk[CLK_TOP_CLK26M]); + if (ret) { + dev_err(dev, "failed to set dsp_sel to clk26m: %d\n", ret); + return ret; + } + + ret = clk_set_parent(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS], + priv->clk[CLK_TOP_MAINPLL_D7_D2]); + if (ret) { + dev_err(dev, "set audio_local_bus failed %d\n", ret); + return ret; + } + + ret = adsp_enable_all_clock(sdev); + if (ret) { + dev_err(dev, "failed to adsp_enable_clock: %d\n", ret); + return ret; + } + } else { + adsp_disable_all_clock(sdev); + } + + return 0; +} + +int adsp_clock_on(struct snd_sof_dev *sdev) +{ + /* Open ADSP clock */ + return adsp_default_clk_init(sdev, 1); +} + +int adsp_clock_off(struct snd_sof_dev *sdev) +{ + /* Close ADSP clock */ + return adsp_default_clk_init(sdev, 0); +} + diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.h b/sound/soc/sof/mediatek/mt8195/mt8195-clk.h new file mode 100644 index 000000000000..9cc0573d5cd2 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2021 MediaTek Corporation. All rights reserved. + * + * Header file for the mt8195 DSP clock definition + */ + +#ifndef __MT8195_CLK_H +#define __MT8195_CLK_H + +struct snd_sof_dev; + +/*DSP clock*/ +enum adsp_clk_id { + CLK_TOP_ADSP, + CLK_TOP_CLK26M, + CLK_TOP_AUDIO_LOCAL_BUS, + CLK_TOP_MAINPLL_D7_D2, + CLK_SCP_ADSP_AUDIODSP, + CLK_TOP_AUDIO_H, + ADSP_CLK_MAX +}; + +int mt8195_adsp_init_clock(struct snd_sof_dev *sdev); +int adsp_clock_on(struct snd_sof_dev *sdev); +int adsp_clock_off(struct snd_sof_dev *sdev); +#endif diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-loader.c b/sound/soc/sof/mediatek/mt8195/mt8195-loader.c new file mode 100644 index 000000000000..ed18d6379e92 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195-loader.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright (c) 2021 Mediatek Corporation. All rights reserved. +// +// Author: YC Hung <yc.hung@mediatek.com> +// +// Hardware interface for mt8195 DSP code loader + +#include <sound/sof.h> +#include "mt8195.h" +#include "../../ops.h" + +void sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr) +{ + /* ADSP bootup base */ + snd_sof_dsp_write(sdev, DSP_REG_BAR, DSP_ALTRESETVEC, boot_addr); + + /* pull high RunStall (set bit3 to 1) */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_RUNSTALL, ADSP_RUNSTALL); + + /* pull high StatVectorSel to use AltResetVec (set bit4 to 1) */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + DSP_RESET_SW, DSP_RESET_SW); + + /* toggle DReset & BReset */ + /* pull high DReset & BReset */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_BRESET_SW | ADSP_DRESET_SW, + ADSP_BRESET_SW | ADSP_DRESET_SW); + + /* pull low DReset & BReset */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_BRESET_SW | ADSP_DRESET_SW, + 0); + + /* Enable PDebug */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_PDEBUGBUS0, + PDEBUG_ENABLE, + PDEBUG_ENABLE); + + /* release RunStall (set bit3 to 0) */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_RUNSTALL, 0); +} + +void sof_hifixdsp_shutdown(struct snd_sof_dev *sdev) +{ + /* Clear to 0 firstly */ + snd_sof_dsp_write(sdev, DSP_REG_BAR, DSP_RESET_SW, 0x0); + + /* RUN_STALL pull high again to reset */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_RUNSTALL, ADSP_RUNSTALL); +} + diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c new file mode 100644 index 000000000000..3ab12f352935 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2021 Mediatek Inc. All rights reserved. +// +// Author: YC Hung <yc.hung@mediatek.com> +// + +/* + * Hardware interface for audio DSP on mt8195 + */ + +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> +#include <linux/module.h> + +#include <sound/sof.h> +#include <sound/sof/xtensa.h> +#include "../../ops.h" +#include "../../sof-of-dev.h" +#include "../../sof-audio.h" +#include "../adsp_helper.h" +#include "mt8195.h" +#include "mt8195-clk.h" + +static int platform_parse_resource(struct platform_device *pdev, void *data) +{ + struct resource *mmio; + struct resource res; + struct device_node *mem_region; + struct device *dev = &pdev->dev; + struct mtk_adsp_chip_info *adsp = data; + int ret; + + mem_region = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!mem_region) { + dev_err(dev, "no dma memory-region phandle\n"); + return -ENODEV; + } + + ret = of_address_to_resource(mem_region, 0, &res); + of_node_put(mem_region); + if (ret) { + dev_err(dev, "of_address_to_resource dma failed\n"); + return ret; + } + + dev_dbg(dev, "DMA %pR\n", &res); + + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "of_reserved_mem_device_init failed\n"); + return ret; + } + + mem_region = of_parse_phandle(dev->of_node, "memory-region", 1); + if (!mem_region) { + dev_err(dev, "no memory-region sysmem phandle\n"); + return -ENODEV; + } + + ret = of_address_to_resource(mem_region, 0, &res); + of_node_put(mem_region); + if (ret) { + dev_err(dev, "of_address_to_resource sysmem failed\n"); + return ret; + } + + adsp->pa_dram = (phys_addr_t)res.start; + adsp->dramsize = resource_size(&res); + if (adsp->pa_dram & DRAM_REMAP_MASK) { + dev_err(dev, "adsp memory(%#x) is not 4K-aligned\n", + (u32)adsp->pa_dram); + return -EINVAL; + } + + if (adsp->dramsize < TOTAL_SIZE_SHARED_DRAM_FROM_TAIL) { + dev_err(dev, "adsp memory(%#x) is not enough for share\n", + adsp->dramsize); + return -EINVAL; + } + + dev_dbg(dev, "dram pbase=%pa, dramsize=%#x\n", + &adsp->pa_dram, adsp->dramsize); + + /* Parse CFG base */ + mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + if (!mmio) { + dev_err(dev, "no ADSP-CFG register resource\n"); + return -ENXIO; + } + /* remap for DSP register accessing */ + adsp->va_cfgreg = devm_ioremap_resource(dev, mmio); + if (IS_ERR(adsp->va_cfgreg)) + return PTR_ERR(adsp->va_cfgreg); + + adsp->pa_cfgreg = (phys_addr_t)mmio->start; + adsp->cfgregsize = resource_size(mmio); + + dev_dbg(dev, "cfgreg-vbase=%p, cfgregsize=%#x\n", + adsp->va_cfgreg, adsp->cfgregsize); + + /* Parse SRAM */ + mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (!mmio) { + dev_err(dev, "no SRAM resource\n"); + return -ENXIO; + } + + adsp->pa_sram = (phys_addr_t)mmio->start; + adsp->sramsize = resource_size(mmio); + if (adsp->sramsize < TOTAL_SIZE_SHARED_SRAM_FROM_TAIL) { + dev_err(dev, "adsp SRAM(%#x) is not enough for share\n", + adsp->sramsize); + return -EINVAL; + } + + dev_dbg(dev, "sram pbase=%pa,%#x\n", &adsp->pa_sram, adsp->sramsize); + + return ret; +} + +static int adsp_sram_power_on(struct device *dev, bool on) +{ + void __iomem *va_dspsysreg; + u32 srampool_con; + + va_dspsysreg = ioremap(ADSP_SRAM_POOL_CON, 0x4); + if (!va_dspsysreg) { + dev_err(dev, "failed to ioremap sram pool base %#x\n", + ADSP_SRAM_POOL_CON); + return -ENOMEM; + } + + srampool_con = readl(va_dspsysreg); + if (on) + writel(srampool_con & ~DSP_SRAM_POOL_PD_MASK, va_dspsysreg); + else + writel(srampool_con | DSP_SRAM_POOL_PD_MASK, va_dspsysreg); + + iounmap(va_dspsysreg); + return 0; +} + +/* Init the basic DSP DRAM address */ +static int adsp_memory_remap_init(struct device *dev, struct mtk_adsp_chip_info *adsp) +{ + void __iomem *vaddr_emi_map; + int offset; + + if (!adsp) + return -ENXIO; + + vaddr_emi_map = devm_ioremap(dev, DSP_EMI_MAP_ADDR, 0x4); + if (!vaddr_emi_map) { + dev_err(dev, "failed to ioremap emi map base %#x\n", + DSP_EMI_MAP_ADDR); + return -ENOMEM; + } + + offset = adsp->pa_dram - DRAM_PHYS_BASE_FROM_DSP_VIEW; + adsp->dram_offset = offset; + offset >>= DRAM_REMAP_SHIFT; + dev_dbg(dev, "adsp->pa_dram %pa, offset %#x\n", &adsp->pa_dram, offset); + writel(offset, vaddr_emi_map); + if (offset != readl(vaddr_emi_map)) { + dev_err(dev, "write emi map fail : %#x\n", readl(vaddr_emi_map)); + return -EIO; + } + + return 0; +} + +static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data) +{ + struct device *dev = &pdev->dev; + struct mtk_adsp_chip_info *adsp = data; + u32 shared_size; + + /* remap shared-dram base to be non-cachable */ + shared_size = TOTAL_SIZE_SHARED_DRAM_FROM_TAIL; + adsp->pa_shared_dram = adsp->pa_dram + adsp->dramsize - shared_size; + if (adsp->va_dram) { + adsp->shared_dram = adsp->va_dram + DSP_DRAM_SIZE - shared_size; + } else { + adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram, + shared_size); + if (!adsp->shared_dram) { + dev_err(dev, "ioremap failed for shared DRAM\n"); + return -ENOMEM; + } + } + dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n", + adsp->shared_dram, &adsp->pa_shared_dram, shared_size); + + return 0; +} + +static int mt8195_run(struct snd_sof_dev *sdev) +{ + u32 adsp_bootup_addr; + + adsp_bootup_addr = SRAM_PHYS_BASE_FROM_DSP_VIEW; + dev_dbg(sdev->dev, "HIFIxDSP boot from base : 0x%08X\n", adsp_bootup_addr); + sof_hifixdsp_boot_sequence(sdev, adsp_bootup_addr); + + return 0; +} + +static int mt8195_dsp_probe(struct snd_sof_dev *sdev) +{ + struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + struct adsp_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sdev->pdata->hw_pdata = priv; + priv->dev = sdev->dev; + priv->sdev = sdev; + + priv->adsp = devm_kzalloc(&pdev->dev, sizeof(struct mtk_adsp_chip_info), GFP_KERNEL); + if (!priv->adsp) + return -ENOMEM; + + ret = platform_parse_resource(pdev, priv->adsp); + if (ret) + return ret; + + ret = mt8195_adsp_init_clock(sdev); + if (ret) { + dev_err(sdev->dev, "mt8195_adsp_init_clock failed\n"); + return -EINVAL; + } + + ret = adsp_clock_on(sdev); + if (ret) { + dev_err(sdev->dev, "adsp_clock_on fail!\n"); + return -EINVAL; + } + + ret = adsp_sram_power_on(sdev->dev, true); + if (ret) { + dev_err(sdev->dev, "adsp_sram_power_on fail!\n"); + goto exit_clk_disable; + } + + ret = adsp_memory_remap_init(&pdev->dev, priv->adsp); + if (ret) { + dev_err(sdev->dev, "adsp_memory_remap_init fail!\n"); + goto err_adsp_sram_power_off; + } + + sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, + priv->adsp->pa_sram, + priv->adsp->sramsize); + if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { + dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n", + &priv->adsp->pa_sram, priv->adsp->sramsize); + ret = -EINVAL; + goto err_adsp_sram_power_off; + } + + sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, + priv->adsp->pa_dram, + priv->adsp->dramsize); + if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { + dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n", + &priv->adsp->pa_dram, priv->adsp->dramsize); + ret = -EINVAL; + goto err_adsp_sram_power_off; + } + priv->adsp->va_dram = sdev->bar[SOF_FW_BLK_TYPE_SRAM]; + + ret = adsp_shared_base_ioremap(pdev, priv->adsp); + if (ret) { + dev_err(sdev->dev, "adsp_shared_base_ioremap fail!\n"); + goto err_adsp_sram_power_off; + } + + sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg; + sdev->bar[DSP_MBOX0_BAR] = priv->adsp->va_mboxreg[0]; + sdev->bar[DSP_MBOX1_BAR] = priv->adsp->va_mboxreg[1]; + sdev->bar[DSP_MBOX2_BAR] = priv->adsp->va_mboxreg[2]; + + sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM; + sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; + + return 0; + +err_adsp_sram_power_off: + adsp_sram_power_on(&pdev->dev, false); +exit_clk_disable: + adsp_clock_off(sdev); + + return ret; +} + +static int mt8195_dsp_remove(struct snd_sof_dev *sdev) +{ + struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + + adsp_sram_power_on(&pdev->dev, false); + adsp_clock_off(sdev); + + return 0; +} + +static int mt8195_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) +{ + struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + int ret; + + /* stall and reset dsp */ + sof_hifixdsp_shutdown(sdev); + + /* power down adsp sram */ + ret = adsp_sram_power_on(&pdev->dev, false); + if (ret) { + dev_err(sdev->dev, "adsp_sram_power_off fail!\n"); + return ret; + } + + /* turn off adsp clock */ + return adsp_clock_off(sdev); +} + +static int mt8195_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + + /* turn on adsp clock */ + ret = adsp_clock_on(sdev); + if (ret) { + dev_err(sdev->dev, "adsp_clock_on fail!\n"); + return ret; + } + + /* power on adsp sram */ + ret = adsp_sram_power_on(sdev->dev, true); + if (ret) + dev_err(sdev->dev, "adsp_sram_power_on fail!\n"); + + return ret; +} + +/* on mt8195 there is 1 to 1 match between type and BAR idx */ +static int mt8195_get_bar_index(struct snd_sof_dev *sdev, u32 type) +{ + return type; +} + +static struct snd_soc_dai_driver mt8195_dai[] = { +{ + .name = "SOF_DL2", + .playback = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_DL3", + .playback = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_UL4", + .capture = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_UL5", + .capture = { + .channels_min = 1, + .channels_max = 2, + }, +}, +}; + +/* mt8195 ops */ +static const struct snd_sof_dsp_ops sof_mt8195_ops = { + /* probe and remove */ + .probe = mt8195_dsp_probe, + .remove = mt8195_dsp_remove, + + /* DSP core boot */ + .run = mt8195_run, + + /* Block IO */ + .block_read = sof_block_read, + .block_write = sof_block_write, + + /* Register IO */ + .write = sof_io_write, + .read = sof_io_read, + .write64 = sof_io_write64, + .read64 = sof_io_read64, + + /* misc */ + .get_bar_index = mt8195_get_bar_index, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + /* firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + + /* Firmware ops */ + .dsp_arch_ops = &sof_xtensa_arch_ops, + + /* DAI drivers */ + .drv = mt8195_dai, + .num_drv = ARRAY_SIZE(mt8195_dai), + + /* PM */ + .suspend = mt8195_dsp_suspend, + .resume = mt8195_dsp_resume, + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + +static const struct sof_dev_desc sof_of_mt8195_desc = { + .default_fw_path = "mediatek/sof", + .default_tplg_path = "mediatek/sof-tplg", + .default_fw_filename = "sof-mt8195.ri", + .nocodec_tplg_filename = "sof-mt8195-nocodec.tplg", + .ops = &sof_mt8195_ops, +}; + +static const struct of_device_id sof_of_mt8195_ids[] = { + { .compatible = "mediatek,mt8195-dsp", .data = &sof_of_mt8195_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_mt8195_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_mt8195_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-mt8195", + .pm = &sof_of_pm, + .of_match_table = sof_of_mt8195_ids, + }, +}; +module_platform_driver(snd_sof_of_mt8195_driver); + +MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.h b/sound/soc/sof/mediatek/mt8195/mt8195.h new file mode 100644 index 000000000000..929424182357 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2021 MediaTek Corporation. All rights reserved. + * + * Header file for the mt8195 DSP register definition + */ + +#ifndef __MT8195_H +#define __MT8195_H + +struct mtk_adsp_chip_info; +struct snd_sof_dev; + +#define DSP_REG_BASE 0x10803000 +#define SCP_CFGREG_BASE 0x10724000 +#define DSP_SYSAO_BASE 0x1080C000 + +/***************************************************************************** + * R E G I S T E R TABLE + *****************************************************************************/ +#define DSP_JTAGMUX 0x0000 +#define DSP_ALTRESETVEC 0x0004 +#define DSP_PDEBUGDATA 0x0008 +#define DSP_PDEBUGBUS0 0x000c +#define PDEBUG_ENABLE BIT(0) +#define DSP_PDEBUGBUS1 0x0010 +#define DSP_PDEBUGINST 0x0014 +#define DSP_PDEBUGLS0STAT 0x0018 +#define DSP_PDEBUGLS1STAT 0x001c +#define DSP_PDEBUGPC 0x0020 +#define DSP_RESET_SW 0x0024 /*reset sw*/ +#define ADSP_BRESET_SW BIT(0) +#define ADSP_DRESET_SW BIT(1) +#define ADSP_RUNSTALL BIT(3) +#define STATVECTOR_SEL BIT(4) +#define DSP_PFAULTBUS 0x0028 +#define DSP_PFAULTINFO 0x002c +#define DSP_GPR00 0x0030 +#define DSP_GPR01 0x0034 +#define DSP_GPR02 0x0038 +#define DSP_GPR03 0x003c +#define DSP_GPR04 0x0040 +#define DSP_GPR05 0x0044 +#define DSP_GPR06 0x0048 +#define DSP_GPR07 0x004c +#define DSP_GPR08 0x0050 +#define DSP_GPR09 0x0054 +#define DSP_GPR0A 0x0058 +#define DSP_GPR0B 0x005c +#define DSP_GPR0C 0x0060 +#define DSP_GPR0D 0x0064 +#define DSP_GPR0E 0x0068 +#define DSP_GPR0F 0x006c +#define DSP_GPR10 0x0070 +#define DSP_GPR11 0x0074 +#define DSP_GPR12 0x0078 +#define DSP_GPR13 0x007c +#define DSP_GPR14 0x0080 +#define DSP_GPR15 0x0084 +#define DSP_GPR16 0x0088 +#define DSP_GPR17 0x008c +#define DSP_GPR18 0x0090 +#define DSP_GPR19 0x0094 +#define DSP_GPR1A 0x0098 +#define DSP_GPR1B 0x009c +#define DSP_GPR1C 0x00a0 +#define DSP_GPR1D 0x00a4 +#define DSP_GPR1E 0x00a8 +#define DSP_GPR1F 0x00ac +#define DSP_TCM_OFFSET 0x00b0 /* not used */ +#define DSP_DDR_OFFSET 0x00b4 /* not used */ +#define DSP_INTFDSP 0x00d0 +#define DSP_INTFDSP_CLR 0x00d4 +#define DSP_SRAM_PD_SW1 0x00d8 +#define DSP_SRAM_PD_SW2 0x00dc +#define DSP_OCD 0x00e0 +#define DSP_RG_DSP_IRQ_POL 0x00f0 /* not used */ +#define DSP_DSP_IRQ_EN 0x00f4 /* not used */ +#define DSP_DSP_IRQ_LEVEL 0x00f8 /* not used */ +#define DSP_DSP_IRQ_STATUS 0x00fc /* not used */ +#define DSP_RG_INT2CIRQ 0x0114 +#define DSP_RG_INT_POL_CTL0 0x0120 +#define DSP_RG_INT_EN_CTL0 0x0130 +#define DSP_RG_INT_LV_CTL0 0x0140 +#define DSP_RG_INT_STATUS0 0x0150 +#define DSP_PDEBUGSTATUS0 0x0200 +#define DSP_PDEBUGSTATUS1 0x0204 +#define DSP_PDEBUGSTATUS2 0x0208 +#define DSP_PDEBUGSTATUS3 0x020c +#define DSP_PDEBUGSTATUS4 0x0210 +#define DSP_PDEBUGSTATUS5 0x0214 +#define DSP_PDEBUGSTATUS6 0x0218 +#define DSP_PDEBUGSTATUS7 0x021c +#define DSP_DSP2PSRAM_PRIORITY 0x0220 /* not used */ +#define DSP_AUDIO_DSP2SPM_INT 0x0224 +#define DSP_AUDIO_DSP2SPM_INT_ACK 0x0228 +#define DSP_AUDIO_DSP_DEBUG_SEL 0x022C +#define DSP_AUDIO_DSP_EMI_BASE_ADDR 0x02E0 /* not used */ +#define DSP_AUDIO_DSP_SHARED_IRAM 0x02E4 +#define DSP_AUDIO_DSP_CKCTRL_P2P_CK_CON 0x02F0 +#define DSP_RG_SEMAPHORE00 0x0300 +#define DSP_RG_SEMAPHORE01 0x0304 +#define DSP_RG_SEMAPHORE02 0x0308 +#define DSP_RG_SEMAPHORE03 0x030C +#define DSP_RG_SEMAPHORE04 0x0310 +#define DSP_RG_SEMAPHORE05 0x0314 +#define DSP_RG_SEMAPHORE06 0x0318 +#define DSP_RG_SEMAPHORE07 0x031C +#define DSP_RESERVED_0 0x03F0 +#define DSP_RESERVED_1 0x03F4 + +/* dsp wdt */ +#define DSP_WDT_MODE 0x0400 + +/* dsp mbox */ +#define DSP_MBOX_IN_CMD 0x00 +#define DSP_MBOX_IN_CMD_CLR 0x04 +#define DSP_MBOX_OUT_CMD 0x1c +#define DSP_MBOX_OUT_CMD_CLR 0x20 +#define DSP_MBOX_IN_MSG0 0x08 +#define DSP_MBOX_IN_MSG1 0x0C +#define DSP_MBOX_OUT_MSG0 0x24 +#define DSP_MBOX_OUT_MSG1 0x28 + +/*dsp sys ao*/ +#define ADSP_SRAM_POOL_CON (DSP_SYSAO_BASE + 0x30) +#define DSP_SRAM_POOL_PD_MASK 0xf +#define DSP_EMI_MAP_ADDR (DSP_SYSAO_BASE + 0x81c) + +/* DSP memories */ +#define MBOX_OFFSET 0x800000 /* DRAM */ +#define MBOX_SIZE 0x1000 /* consistent with which in memory.h of sof fw */ +#define DSP_DRAM_SIZE 0x1000000 /* 16M */ + +#define DSP_REG_BAR 4 +#define DSP_MBOX0_BAR 5 +#define DSP_MBOX1_BAR 6 +#define DSP_MBOX2_BAR 7 + +#define TOTAL_SIZE_SHARED_SRAM_FROM_TAIL 0x0 + +#define SIZE_SHARED_DRAM_DL 0x40000 /*Shared buffer for Downlink*/ +#define SIZE_SHARED_DRAM_UL 0x40000 /*Shared buffer for Uplink*/ + +#define TOTAL_SIZE_SHARED_DRAM_FROM_TAIL \ + (SIZE_SHARED_DRAM_DL + SIZE_SHARED_DRAM_UL) + +#define SRAM_PHYS_BASE_FROM_DSP_VIEW 0x40000000 /* MT8195 DSP view */ +#define DRAM_PHYS_BASE_FROM_DSP_VIEW 0x60000000 /* MT8195 DSP view */ + +/*remap dram between AP and DSP view, 4KB aligned*/ +#define DRAM_REMAP_SHIFT 12 +#define DRAM_REMAP_MASK (BIT(DRAM_REMAP_SHIFT) - 1) + +void sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr); +void sof_hifixdsp_shutdown(struct snd_sof_dev *sdev); +#endif diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 160b88a2d59f..235e2ef72178 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -142,25 +142,46 @@ void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, } EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); -void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) +/** + * snd_sof_dsp_panic - handle a received DSP panic message + * @sdev: Pointer to the device's sdev + * @offset: offset of panic information + * @non_recoverable: the panic is fatal, no recovery will be done by the caller + */ +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable) { - dev_err(sdev->dev, "error : DSP panic!\n"); - /* - * check if DSP is not ready and did not set the dsp_oops_offset. - * if the dsp_oops_offset is not set, set it from the panic message. - * Also add a check to memory window setting with panic message. + * if DSP is not ready and the dsp_oops_offset is not yet set, use the + * offset from the panic message. */ if (!sdev->dsp_oops_offset) sdev->dsp_oops_offset = offset; - else - dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n", - sdev->dsp_oops_offset, offset); - /* We want to see the DSP panic! */ - sdev->dbg_dump_printed = false; + /* + * Print warning if the offset from the panic message differs from + * dsp_oops_offset + */ + if (sdev->dsp_oops_offset != offset) + dev_warn(sdev->dev, + "%s: dsp_oops_offset %zu differs from panic offset %u\n", + __func__, sdev->dsp_oops_offset, offset); - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); - snd_sof_trace_notify_for_error(sdev); + /* + * Set the fw_state to crashed only in case of non recoverable DSP panic + * event. + * Use different message within the snd_sof_dsp_dbg_dump() depending on + * the non_recoverable flag. + */ + sdev->dbg_dump_printed = false; + if (non_recoverable) { + snd_sof_dsp_dbg_dump(sdev, "DSP panic!", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + sof_set_fw_state(sdev, SOF_FW_CRASHED); + snd_sof_trace_notify_for_error(sdev); + } else { + snd_sof_dsp_dbg_dump(sdev, + "DSP panic (recovery will be attempted)", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + } } EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 09bf38fdfb8a..ffe7456e7713 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -72,35 +72,68 @@ static inline int snd_sof_dsp_reset(struct snd_sof_dev *sdev) return 0; } -/* dsp core power up/power down */ -static inline int snd_sof_dsp_core_power_up(struct snd_sof_dev *sdev, - unsigned int core_mask) +/* dsp core get/put */ +static inline int snd_sof_dsp_core_get(struct snd_sof_dev *sdev, int core) { - int ret = 0; + if (core > sdev->num_cores - 1) { + dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core, + sdev->num_cores); + return -EINVAL; + } + + if (sof_ops(sdev)->core_get) { + int ret; + + /* if current ref_count is > 0, increment it and return */ + if (sdev->dsp_core_ref_count[core] > 0) { + sdev->dsp_core_ref_count[core]++; + return 0; + } + + /* power up the core */ + ret = sof_ops(sdev)->core_get(sdev, core); + if (ret < 0) + return ret; + + /* increment ref_count */ + sdev->dsp_core_ref_count[core]++; - core_mask &= ~sdev->enabled_cores_mask; - if (sof_ops(sdev)->core_power_up && core_mask) { - ret = sof_ops(sdev)->core_power_up(sdev, core_mask); - if (!ret) - sdev->enabled_cores_mask |= core_mask; + /* and update enabled_cores_mask */ + sdev->enabled_cores_mask |= BIT(core); + + dev_dbg(sdev->dev, "Core %d powered up\n", core); } - return ret; + return 0; } -static inline int snd_sof_dsp_core_power_down(struct snd_sof_dev *sdev, - unsigned int core_mask) +static inline int snd_sof_dsp_core_put(struct snd_sof_dev *sdev, int core) { - int ret = 0; + if (core > sdev->num_cores - 1) { + dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core, + sdev->num_cores); + return -EINVAL; + } + + if (sof_ops(sdev)->core_put) { + int ret; - core_mask &= sdev->enabled_cores_mask; - if (sof_ops(sdev)->core_power_down && core_mask) { - ret = sof_ops(sdev)->core_power_down(sdev, core_mask); - if (!ret) - sdev->enabled_cores_mask &= ~core_mask; + /* decrement ref_count and return if it is > 0 */ + if (--(sdev->dsp_core_ref_count[core]) > 0) + return 0; + + /* power down the core */ + ret = sof_ops(sdev)->core_put(sdev, core); + if (ret < 0) + return ret; + + /* and update enabled_cores_mask */ + sdev->enabled_cores_mask &= ~BIT(core); + + dev_dbg(sdev->dev, "Core %d powered down\n", core); } - return ret; + return 0; } /* pre/post fw load */ @@ -241,7 +274,7 @@ snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev, } /* debug */ -void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags); +void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags); static inline int snd_sof_debugfs_add_region_item(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size, @@ -454,6 +487,16 @@ snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, return 0; } +/* pcm ack */ +static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + if (sof_ops(sdev) && sof_ops(sdev)->pcm_ack) + return sof_ops(sdev)->pcm_ack(sdev, substream); + + return 0; +} + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) static inline int snd_sof_probe_compr_assign(struct snd_sof_dev *sdev, @@ -514,15 +557,17 @@ snd_sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) sof_ops(sdev)->machine_unregister(sdev, pdata); } -static inline void +static inline struct snd_soc_acpi_mach * snd_sof_machine_select(struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->machine_select) - sof_ops(sdev)->machine_select(sdev); + return sof_ops(sdev)->machine_select(sdev); + + return NULL; } static inline void -snd_sof_set_mach_params(const struct snd_soc_acpi_mach *mach, +snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->set_mach_params) @@ -598,5 +643,5 @@ int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 target, u32 timeout_ms, u32 interval_us); -void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset); +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable); #endif diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index fa0bfcd2474e..37fb8e6cd493 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -100,14 +100,16 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); -static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, - struct snd_sof_dev *sdev, - struct snd_sof_pcm *spcm) +int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm) { struct sof_ipc_stream stream; struct sof_ipc_reply reply; int ret; + if (!spcm->prepared[substream->stream]) + return 0; + stream.hdr.size = sizeof(stream); stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; stream.comp_id = spcm->stream[substream->stream].comp_id; @@ -179,11 +181,9 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. */ - if (spcm->prepared[substream->stream]) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; - } + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); @@ -299,24 +299,26 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); - if (spcm->prepared[substream->stream]) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - err = ret; - } - - ret = sof_widget_list_free(sdev, spcm, substream->stream); + /* free PCM in the DSP */ + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); if (ret < 0) err = ret; - cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); + /* stop DMA */ ret = snd_sof_pcm_platform_hw_free(sdev, substream); if (ret < 0) { dev_err(component->dev, "error: platform hw free failed\n"); err = ret; } + /* free the DAPM widget list */ + ret = sof_widget_list_free(sdev, spcm, substream->stream); + if (ret < 0) + err = ret; + + cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); + return err; } @@ -393,26 +395,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; break; - case SNDRV_PCM_TRIGGER_RESUME: - if (spcm->stream[substream->stream].suspend_ignored) { - /* - * this case will be triggered when INFO_RESUME is - * supported, no need to resume streams that remained - * enabled in D0ix. - */ - spcm->stream[substream->stream].suspend_ignored = false; - return 0; - } - - /* set up hw_params */ - ret = sof_pcm_prepare(component, substream); - if (ret < 0) { - dev_err(component->dev, - "error: failed to set up hw_params upon resume\n"); - return ret; - } - - fallthrough; case SNDRV_PCM_TRIGGER_START: if (spcm->stream[substream->stream].suspend_ignored) { /* @@ -467,13 +449,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, /* free PCM if reset_hw_params is set and the STOP IPC is successful */ if (!ret && reset_hw_params) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, + free_widget_list); if (ret < 0) return ret; - - /* free widget list only for SUSPEND trigger */ - if (free_widget_list) - ret = sof_widget_list_free(sdev, spcm, substream->stream); } return ret; @@ -814,6 +793,18 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa "channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_MEDIATEK_AFE: + rate->min = dai->dai_config->afe.rate; + rate->max = dai->dai_config->afe.rate; + channels->min = dai->dai_config->afe.channels; + channels->max = dai->dai_config->afe.channels; + + dev_dbg(component->dev, + "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; case SOF_DAI_IMX_SAI: rate->min = dai->dai_config->sai.fsync_rate; rate->max = dai->dai_config->sai.fsync_rate; @@ -826,6 +817,42 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa "channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_AMD_BT: + rate->min = dai->dai_config->acpbt.fsync_rate; + rate->max = dai->dai_config->acpbt.fsync_rate; + channels->min = dai->dai_config->acpbt.tdm_slots; + channels->max = dai->dai_config->acpbt.tdm_slots; + + dev_dbg(component->dev, + "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "AMD_BT channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_SP: + rate->min = dai->dai_config->acpsp.fsync_rate; + rate->max = dai->dai_config->acpsp.fsync_rate; + channels->min = dai->dai_config->acpsp.tdm_slots; + channels->max = dai->dai_config->acpsp.tdm_slots; + + dev_dbg(component->dev, + "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "AMD_SP channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_DMIC: + rate->min = dai->dai_config->acpdmic.fsync_rate; + rate->max = dai->dai_config->acpdmic.fsync_rate; + channels->min = dai->dai_config->acpdmic.tdm_slots; + channels->max = dai->dai_config->acpdmic.tdm_slots; + + dev_dbg(component->dev, + "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "AMD_DMIC channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; default: dev_err(component->dev, "error: invalid DAI type %d\n", dai->dai_config->type); @@ -869,6 +896,14 @@ static void sof_pcm_remove(struct snd_soc_component *component) snd_soc_tplg_component_remove(component); } +static int sof_pcm_ack(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + + return snd_sof_pcm_platform_ack(sdev, substream); +} + void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) { struct snd_soc_component_driver *pd = &sdev->plat_drv; @@ -887,6 +922,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->hw_free = sof_pcm_hw_free; pd->trigger = sof_pcm_trigger; pd->pointer = sof_pcm_pointer; + pd->ack = sof_pcm_ack; #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) pd->compress_ops = &sof_probe_compressed_ops; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index ac8ae6e422a7..197a88695fef 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -130,6 +130,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) dev_err(sdev->dev, "error: failed to load DSP firmware after resume %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return ret; } @@ -144,6 +145,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) dev_err(sdev->dev, "error: failed to boot DSP firmware after resume %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return ret; } @@ -312,6 +314,14 @@ int snd_sof_prepare(struct device *dev) /* will suspend to S3 by default */ sdev->system_suspend_target = SOF_SUSPEND_S3; + /* + * if the firmware is crashed or boot failed then we try to aim for S3 + * to reboot the firmware + */ + if (sdev->fw_state == SOF_FW_CRASHED || + sdev->fw_state == SOF_FW_BOOT_FAILED) + return 0; + if (!desc->use_acpi_target_states) return 0; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 7cbe757c1fe2..9e76b796502f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -14,29 +14,12 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) { - int ipc_cmd, ctrl_type; int ret; /* reset readback offset for scontrol */ scontrol->readback_offset = 0; - /* notify DSP of kcontrol values */ - switch (scontrol->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_ENUM: - case SOF_CTRL_CMD_SWITCH: - ipc_cmd = SOF_IPC_COMP_SET_VALUE; - ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; - break; - case SOF_CTRL_CMD_BINARY: - ipc_cmd = SOF_IPC_COMP_SET_DATA; - ctrl_type = SOF_CTRL_TYPE_DATA_SET; - break; - default: - return 0; - } - - ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd, ctrl_type, scontrol->cmd, true); + ret = snd_sof_ipc_set_get_comp_data(scontrol, true); if (ret < 0) dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n", scontrol->comp_id); @@ -57,7 +40,7 @@ static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *da } /* set NONE flag to clear all previous settings */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_NONE); + config->flags = SOF_DAI_CONFIG_FLAGS_NONE; ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, &reply, sizeof(reply)); @@ -76,12 +59,26 @@ static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_wi /* set up all controls for the widget */ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) if (scontrol->comp_id == swidget->comp_id) { + /* set kcontrol data in DSP */ ret = sof_kcontrol_setup(sdev, scontrol); if (ret < 0) { dev_err(sdev->dev, "error: fail to set up kcontrols for widget %s\n", swidget->widget->name); return ret; } + + /* + * Read back the data from the DSP for static widgets. This is particularly + * useful for binary kcontrols associated with static pipeline widgets to + * initialize the data size to match that in the DSP. + */ + if (swidget->dynamic_pipeline_widget) + continue; + + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + if (ret < 0) + dev_warn(sdev->dev, "Failed kcontrol get for control in widget %s\n", + swidget->widget->name); } return 0; @@ -106,7 +103,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) .id = swidget->comp_id, }; struct sof_ipc_reply reply; - int ret; + int ret, ret1, core; if (!swidget->private) return 0; @@ -115,32 +112,59 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (--swidget->use_count) return 0; + core = swidget->core; + switch (swidget->id) { case snd_soc_dapm_scheduler: + { + const struct sof_ipc_pipe_new *pipeline = swidget->private; + + core = pipeline->core; ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE; break; + } case snd_soc_dapm_buffer: ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE; break; + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_dai *dai = swidget->private; + + dai->configured = false; + fallthrough; + } default: ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE; break; } + /* continue to disable core even if IPC fails */ ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), &reply, sizeof(reply)); - if (ret < 0) { + if (ret < 0) dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name); - swidget->use_count++; - return ret; + + /* + * disable widget core. continue to route setup status and complete flag + * even if this fails and return the appropriate error + */ + ret1 = snd_sof_dsp_core_put(sdev, core); + if (ret1 < 0) { + dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n", + core, swidget->widget->name); + if (!ret) + ret = ret1; } /* reset route setup status for all routes that contain this widget */ sof_reset_route_setup_status(sdev, swidget); swidget->complete = 0; - dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); - return 0; + if (!ret) + dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); + + return ret; } EXPORT_SYMBOL(sof_widget_free); @@ -153,6 +177,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) struct snd_sof_dai *dai; size_t ipc_size; int ret; + int core; /* skip if there is no private data */ if (!swidget->private) @@ -162,10 +187,18 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (++swidget->use_count > 1) return 0; - ret = sof_pipeline_core_enable(sdev, swidget); + /* set core ID */ + core = swidget->core; + if (swidget->id == snd_soc_dapm_scheduler) { + pipeline = swidget->private; + core = pipeline->core; + } + + /* enable widget core */ + ret = snd_sof_dsp_core_get(sdev, core); if (ret < 0) { - dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n", - ret, swidget->widget->name); + dev_err(sdev->dev, "error: failed to enable target core for widget %s\n", + swidget->widget->name); goto use_count_dec; } @@ -174,8 +207,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) case snd_soc_dapm_dai_out: ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext); comp = kzalloc(ipc_size, GFP_KERNEL); - if (!comp) - return -ENOMEM; + if (!comp) { + ret = -ENOMEM; + goto core_put; + } dai = swidget->private; dai->configured = false; @@ -190,20 +225,26 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (ret < 0) { dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name); - goto use_count_dec; + goto core_put; } ret = sof_dai_config_setup(sdev, dai); if (ret < 0) { dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n", swidget->widget->name); + + /* + * widget use_count and core ref_count will both be decremented by + * sof_widget_free() + */ sof_widget_free(sdev, swidget); return ret; } break; case snd_soc_dapm_scheduler: pipeline = swidget->private; - ret = sof_load_pipeline_ipc(sdev, pipeline, &r); + ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, + sizeof(*pipeline), &r, sizeof(r)); break; default: hdr = swidget->private; @@ -213,7 +254,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) } if (ret < 0) { dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name); - goto use_count_dec; + goto core_put; } /* restore kcontrols for widget */ @@ -221,6 +262,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (ret < 0) { dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n", swidget->widget->name); + /* + * widget use_count and core ref_count will both be decremented by + * sof_widget_free() + */ sof_widget_free(sdev, swidget); return ret; } @@ -229,6 +274,8 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) return 0; +core_put: + snd_sof_dsp_core_put(sdev, core); use_count_dec: swidget->use_count--; return ret; @@ -595,16 +642,25 @@ const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) { + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; int ret; /* restore pipeline components */ - list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &sdev->widget_list, list) { /* only set up the widgets belonging to static pipelines */ if (!verify && swidget->dynamic_pipeline_widget) continue; + /* + * For older firmware, skip scheduler widgets in this loop, + * sof_widget_setup() will be called in the 'complete pipeline' loop + */ + if (v->abi_version < SOF_ABI_VER(3, 19, 0) && + swidget->id == snd_soc_dapm_scheduler) + continue; + /* update DAI config. The IPC will be sent in sof_widget_setup() */ if (WIDGET_IS_DAI(swidget->id)) { struct snd_sof_dai *dai = swidget->private; @@ -652,6 +708,12 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) if (!verify && swidget->dynamic_pipeline_widget) continue; + if (v->abi_version < SOF_ABI_VER(3, 19, 0)) { + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) + return ret; + } + swidget->complete = snd_sof_complete_pipeline(sdev, swidget); break; @@ -663,12 +725,81 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) return 0; } +int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir, bool free_widget_list) +{ + int ret; + + /* Send PCM_FREE IPC to reset pipeline */ + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; + + /* stop the DMA */ + ret = snd_sof_pcm_platform_hw_free(sdev, substream); + if (ret < 0) + return ret; + + /* free widget list */ + if (free_widget_list) { + ret = sof_widget_list_free(sdev, spcm, dir); + if (ret < 0) + dev_err(sdev->dev, "failed to free widgets during suspend\n"); + } + + return ret; +} + +/* + * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that + * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. + */ +static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) +{ + struct snd_sof_widget *swidget; + struct snd_sof_pcm *spcm; + int dir, ret; + + /* + * free all PCMs and their associated DAPM widgets if their connected DAPM widget + * list is not NULL. This should only be true for paused streams at this point. + * This is equivalent to the handling of FE DAI suspend trigger for running streams. + */ + list_for_each_entry(spcm, &sdev->pcm_list, list) + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream = spcm->stream[dir].substream; + + if (!substream || !substream->runtime) + continue; + + if (spcm->stream[dir].list) { + ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); + if (ret < 0) + return ret; + } + } + + /* + * free any left over DAI widgets. This is equivalent to the handling of suspend trigger + * for the BE DAI for running streams. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) + if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + return 0; +} + /* - * This function doesn't free widgets during suspend. It only resets the set up status for all - * routes and use_count for all widgets. + * For older firmware, this function doesn't free widgets for static pipelines during suspend. + * It only resets use_count for all widgets. */ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) { + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; int ret; @@ -676,12 +807,18 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) /* * This function is called during suspend and for one-time topology verification during * first boot. In both cases, there is no need to protect swidget->use_count and - * sroute->setup because during suspend all streams are suspended and during topology - * loading the sound card unavailable to open PCMs. + * sroute->setup because during suspend all running streams are suspended and during + * topology loading the sound card unavailable to open PCMs. */ - list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { - if (!verify) { + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->dynamic_pipeline_widget) + continue; + + /* Do not free widgets for static pipelines with FW ABI older than 3.19 */ + if (!verify && !swidget->dynamic_pipeline_widget && + v->abi_version < SOF_ABI_VER(3, 19, 0)) { swidget->use_count = 0; + swidget->complete = 0; continue; } @@ -690,6 +827,19 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) return ret; } + /* + * Tear down all pipelines associated with PCMs that did not get suspended + * and unset the prepare flag so that they can be set up again during resume. + * Skip this step for older firmware. + */ + if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) { + ret = sof_tear_down_left_over_pipelines(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to tear down paused pipelines\n"); + return ret; + } + } + list_for_each_entry(sroute, &sdev->route_list, list) sroute->setup = false; @@ -877,9 +1027,10 @@ int sof_machine_check(struct snd_sof_dev *sdev) if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) { /* find machine */ - snd_sof_machine_select(sdev); - if (sof_pdata->machine) { - snd_sof_set_mach_params(sof_pdata->machine, sdev); + mach = snd_sof_machine_select(sdev); + if (mach) { + sof_pdata->machine = mach; + snd_sof_set_mach_params(mach, sdev); return 0; } @@ -901,7 +1052,7 @@ int sof_machine_check(struct snd_sof_dev *sdev) sof_pdata->tplg_filename = desc->nocodec_tplg_filename; sof_pdata->machine = mach; - snd_sof_set_mach_params(sof_pdata->machine, sdev); + snd_sof_set_mach_params(mach, sdev); return 0; } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 05e98e231b85..f3009e6b91a1 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -74,7 +74,6 @@ struct snd_sof_control { u32 readback_offset; /* offset to mmapped data if used */ struct sof_ipc_ctrl_data *control_data; u32 size; /* cdata size */ - enum sof_ipc_ctrl_cmd cmd; u32 *volume_table; /* volume table computed from tlv data*/ struct list_head list; /* list in sdev control list */ @@ -185,12 +184,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file); int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); -int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, - struct sof_ipc_pipe_new *pipeline, - struct sof_ipc_comp_reply *r); -int sof_pipeline_core_enable(struct snd_sof_dev *sdev, - const struct snd_sof_widget *swidget); - /* * Stream IPC */ @@ -245,11 +238,7 @@ static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } /* * Mixer IPC */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - u32 ipc_cmd, - enum sof_ipc_ctrl_type ctrl_type, - enum sof_ipc_ctrl_cmd ctrl_cmd, - bool send); +int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set); /* DAI link fixup */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); @@ -271,4 +260,8 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); /* PCM */ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); +int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm); +int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir, bool free_widget_list); #endif diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index 885430a42226..e3718638f9ce 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -11,8 +11,8 @@ #include <linux/pm_runtime.h> #include <sound/sof.h> +#include "sof-of-dev.h" #include "ops.h" -#include "imx/imx-ops.h" static char *fw_path; module_param(fw_path, charp, 0444); @@ -22,56 +22,26 @@ static char *tplg_path; module_param(tplg_path, charp, 0444); MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology."); -/* platform specific devices */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8) -static struct sof_dev_desc sof_of_imx8qxp_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8x.ri", - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8x_ops, -}; - -static struct sof_dev_desc sof_of_imx8qm_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8.ri", - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8_ops, -}; -#endif - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M) -static struct sof_dev_desc sof_of_imx8mp_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8m.ri", - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8m_ops, -}; -#endif - -static const struct dev_pm_ops sof_of_pm = { +const struct dev_pm_ops sof_of_pm = { .prepare = snd_sof_prepare, .complete = snd_sof_complete, SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, NULL) }; +EXPORT_SYMBOL(sof_of_pm); static void sof_of_probe_complete(struct device *dev) { /* allow runtime_pm */ pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); } -static int sof_of_probe(struct platform_device *pdev) +int sof_of_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct sof_dev_desc *desc; @@ -112,8 +82,9 @@ static int sof_of_probe(struct platform_device *pdev) /* call sof helper for DSP hardware probe */ return snd_sof_device_probe(dev, sof_pdata); } +EXPORT_SYMBOL(sof_of_probe); -static int sof_of_remove(struct platform_device *pdev) +int sof_of_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); @@ -122,29 +93,6 @@ static int sof_of_remove(struct platform_device *pdev) return 0; } - -static const struct of_device_id sof_of_ids[] = { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8) - { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc}, - { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc}, -#endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M) - { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc}, -#endif - { } -}; -MODULE_DEVICE_TABLE(of, sof_of_ids); - -/* DT driver definition */ -static struct platform_driver snd_sof_of_driver = { - .probe = sof_of_probe, - .remove = sof_of_remove, - .driver = { - .name = "sof-audio-of", - .pm = &sof_of_pm, - .of_match_table = sof_of_ids, - }, -}; -module_platform_driver(snd_sof_of_driver); +EXPORT_SYMBOL(sof_of_remove); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-of-dev.h b/sound/soc/sof/sof-of-dev.h new file mode 100644 index 000000000000..4e0f6588dad9 --- /dev/null +++ b/sound/soc/sof/sof-of-dev.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright 2021 NXP + */ + +#ifndef __SOUND_SOC_SOF_OF_H +#define __SOUND_SOC_SOF_OF_H + +extern const struct dev_pm_ops sof_of_pm; + +int sof_of_probe(struct platform_device *pdev); +int sof_of_remove(struct platform_device *pdev); + +#endif diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index bc9e70765678..20c6ca37dbc4 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -59,22 +59,23 @@ static const struct dmi_system_id sof_tplg_table[] = { }, .driver_data = "sof-adl-rt5682-ssp0-max98373-ssp2.tplg", }, + { + .callback = sof_tplg_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"), + }, + .driver_data = "sof-adl-max98390-ssp2-rt5682-ssp0.tplg", + }, + {} }; static const struct dmi_system_id community_key_platforms[] = { { - .ident = "Up Squared", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), - DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"), - } - }, - { - .ident = "Up Extreme", + .ident = "Up boards", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), - DMI_MATCH(DMI_BOARD_NAME, "UP-WHL01"), } }, { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index ba341b1bda0c..087935192ce8 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -20,7 +20,7 @@ #include <uapi/sound/sof/fw.h> #include <sound/sof/ext_manifest.h> -/* debug flags */ +/* Flag definitions used in sof_core_debug (sof_debug module parameter) */ #define SOF_DBG_ENABLE_TRACE BIT(0) #define SOF_DBG_RETAIN_CTX BIT(1) /* prevent DSP D3 on FW exception */ #define SOF_DBG_VERIFY_TPLG BIT(2) /* verify topology during load */ @@ -35,14 +35,16 @@ */ #define SOF_DBG_PRINT_ALL_DUMPS BIT(6) /* Print all ipc and dsp dumps */ +/* Flag definitions used for controlling the DSP dump behavior */ #define SOF_DBG_DUMP_REGS BIT(0) #define SOF_DBG_DUMP_MBOX BIT(1) #define SOF_DBG_DUMP_TEXT BIT(2) #define SOF_DBG_DUMP_PCI BIT(3) -#define SOF_DBG_DUMP_OPTIONAL BIT(4) /* only dump if SOF_DBG_PRINT_ALL_DUMPS is set */ +/* Output this dump (at the DEBUG level) only when SOF_DBG_PRINT_ALL_DUMPS is set */ +#define SOF_DBG_DUMP_OPTIONAL BIT(4) /* global debug state set by SOF_DBG_ flags */ -extern int sof_core_debug; +bool sof_debug_check_flag(int mask); /* max BARs mmaped devices can use */ #define SND_SOF_BARS 8 @@ -71,6 +73,9 @@ extern int sof_core_debug; /* So far the primary core on all DSPs has ID 0 */ #define SOF_DSP_PRIMARY_CORE 0 +/* max number of DSP cores */ +#define SOF_MAX_DSP_NUM_CORES 8 + /* DSP power state */ enum sof_dsp_power_states { SOF_DSP_PM_D0, @@ -127,10 +132,8 @@ struct snd_sof_dsp_ops { int (*run)(struct snd_sof_dev *sof_dev); /* mandatory */ int (*stall)(struct snd_sof_dev *sof_dev, unsigned int core_mask); /* optional */ int (*reset)(struct snd_sof_dev *sof_dev); /* optional */ - int (*core_power_up)(struct snd_sof_dev *sof_dev, - unsigned int core_mask); /* optional */ - int (*core_power_down)(struct snd_sof_dev *sof_dev, - unsigned int core_mask); /* optional */ + int (*core_get)(struct snd_sof_dev *sof_dev, int core); /* optional */ + int (*core_put)(struct snd_sof_dev *sof_dev, int core); /* optional */ /* * Register IO: only used by respective drivers themselves, @@ -206,6 +209,9 @@ struct snd_sof_dsp_ops { snd_pcm_uframes_t (*pcm_pointer)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ + /* pcm ack */ + int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) /* Except for probe_pointer, all probe ops are mandatory */ int (*probe_assign)(struct snd_sof_dev *sdev, @@ -289,8 +295,8 @@ struct snd_sof_dsp_ops { void *pdata); /* optional */ void (*machine_unregister)(struct snd_sof_dev *sdev, void *pdata); /* optional */ - void (*machine_select)(struct snd_sof_dev *sdev); /* optional */ - void (*set_mach_params)(const struct snd_soc_acpi_mach *mach, + struct snd_soc_acpi_mach * (*machine_select)(struct snd_sof_dev *sdev); /* optional */ + void (*set_mach_params)(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* optional */ /* DAI ops */ @@ -305,8 +311,8 @@ struct snd_sof_dsp_ops { /* DSP architecture specific callbacks for oops and stack dumps */ struct dsp_arch_ops { - void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops); - void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops, + void (*dsp_oops)(struct snd_sof_dev *sdev, const char *level, void *oops); + void (*dsp_stack)(struct snd_sof_dev *sdev, const char *level, void *oops, u32 *stack, u32 stack_words); }; @@ -326,6 +332,10 @@ struct snd_sof_dfsentry { #if ENABLE_DEBUGFS_CACHEBUF char *cache_buf; /* buffer to cache the contents of debugfs memory */ #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) + void *msg_inject_tx; + void *msg_inject_rx; +#endif struct snd_sof_dev *sdev; struct list_head list; /* list in sdev dfsentry list */ union { @@ -367,15 +377,6 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; -enum snd_sof_fw_state { - SOF_FW_BOOT_NOT_STARTED = 0, - SOF_FW_BOOT_PREPARE, - SOF_FW_BOOT_IN_PROGRESS, - SOF_FW_BOOT_FAILED, - SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */ - SOF_FW_BOOT_COMPLETE, -}; - /* * SOF Device Level. */ @@ -400,7 +401,7 @@ struct snd_sof_dev { /* DSP firmware boot */ wait_queue_head_t boot_wait; - enum snd_sof_fw_state fw_state; + enum sof_fw_state fw_state; bool first_boot; /* work queue in case the probe is implemented in two steps */ @@ -473,6 +474,18 @@ struct snd_sof_dev { bool msi_enabled; + /* DSP core context */ + u32 num_cores; + + /* + * ref count per core that will be modified during system suspend/resume and during pcm + * hw_params/hw_free. This doesn't need to be protected with a mutex because pcm + * hw_params/hw_free are already protected by the PCM mutex in the ALSA framework in + * sound/core/ when streams are active and during system suspend/resume, streams are + * already suspended. + */ + int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES]; + void *private; /* core does not touch this */ }; @@ -515,6 +528,7 @@ void snd_sof_fw_unload(struct snd_sof_dev *sdev); */ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev); void snd_sof_ipc_free(struct snd_sof_dev *sdev); +void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev); void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev); int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev, @@ -527,6 +541,11 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes); int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev); +static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_id) +{ + snd_sof_ipc_get_reply(sdev); + snd_sof_ipc_reply(sdev, msg_id); +} /* * Trace/debug @@ -542,10 +561,10 @@ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn); void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); -void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, - u32 tracep_code, void *oops, - struct sof_ipc_panic_info *panic_info, - void *stack, size_t stack_words); +void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level, + u32 panic_code, u32 tracep_code, void *oops, + struct sof_ipc_panic_info *panic_info, + void *stack, size_t stack_words); int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev); int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev); @@ -556,16 +575,17 @@ int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, /* * DSP Architectures. */ -static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, - u32 stack_words) +static inline void sof_stack(struct snd_sof_dev *sdev, const char *level, + void *oops, u32 *stack, u32 stack_words) { - sof_dsp_arch_ops(sdev)->dsp_stack(sdev, oops, stack, stack_words); + sof_dsp_arch_ops(sdev)->dsp_stack(sdev, level, oops, stack, + stack_words); } -static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) +static inline void sof_oops(struct snd_sof_dev *sdev, const char *level, void *oops) { if (sof_dsp_arch_ops(sdev)->dsp_oops) - sof_dsp_arch_ops(sdev)->dsp_oops(sdev, oops); + sof_dsp_arch_ops(sdev)->dsp_oops(sdev, level, oops); } extern const struct dsp_arch_ops sof_xtensa_arch_ops; @@ -574,7 +594,7 @@ extern const struct dsp_arch_ops sof_xtensa_arch_ops; * Firmware state tracking */ static inline void sof_set_fw_state(struct snd_sof_dev *sdev, - enum snd_sof_fw_state new_state) + enum sof_fw_state new_state) { if (sdev->fw_state == new_state) return; diff --git a/sound/soc/sof/sof-probes.c b/sound/soc/sof/sof-probes.c index 5586af9f1a25..c79026cdb8c7 100644 --- a/sound/soc/sof/sof-probes.c +++ b/sound/soc/sof/sof-probes.c @@ -321,7 +321,7 @@ static int sof_probe_compr_pointer(struct snd_compr_stream *cstream, return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai); } -struct snd_soc_cdai_ops sof_probe_compr_ops = { +const struct snd_soc_cdai_ops sof_probe_compr_ops = { .startup = sof_probe_compr_startup, .shutdown = sof_probe_compr_shutdown, .set_params = sof_probe_compr_set_params, diff --git a/sound/soc/sof/sof-probes.h b/sound/soc/sof/sof-probes.h index 35e1dd8d9e03..4a1ed2942d28 100644 --- a/sound/soc/sof/sof-probes.h +++ b/sound/soc/sof/sof-probes.h @@ -32,7 +32,7 @@ int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, unsigned int *buffer_id, size_t num_buffer_id); -extern struct snd_soc_cdai_ops sof_probe_compr_ops; +extern const struct snd_soc_cdai_ops sof_probe_compr_ops; extern const struct snd_compress_ops sof_probe_compressed_ops; #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index bb9e62bbe5db..e72dcae5e7ee 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -376,6 +376,10 @@ static const struct sof_dai_types sof_dais[] = { {"ALH", SOF_DAI_INTEL_ALH}, {"SAI", SOF_DAI_IMX_SAI}, {"ESAI", SOF_DAI_IMX_ESAI}, + {"ACP", SOF_DAI_AMD_BT}, + {"ACPSP", SOF_DAI_AMD_SP}, + {"ACPDMIC", SOF_DAI_AMD_DMIC}, + {"AFE", SOF_DAI_MEDIATEK_AFE}, }; static enum sof_ipc_dai_type find_dai(const char *name) @@ -803,6 +807,19 @@ static const struct sof_topology_token led_tokens[] = { get_token_u32, offsetof(struct snd_sof_led_control, direction), 0}, }; +/* AFE */ +static const struct sof_topology_token afe_tokens[] = { + {SOF_TKN_MEDIATEK_AFE_RATE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, rate), 0}, + {SOF_TKN_MEDIATEK_AFE_CH, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, channels), 0}, + {SOF_TKN_MEDIATEK_AFE_FORMAT, + SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_dai_mtk_afe_params, format), 0}, +}; + static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, void *object, const struct sof_topology_token *tokens, @@ -1073,11 +1090,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, /* set cmd for mixer control */ if (le32_to_cpu(mc->max) == 1) { - scontrol->cmd = SOF_CTRL_CMD_SWITCH; + scontrol->control_data->cmd = SOF_CTRL_CMD_SWITCH; goto skip; } - scontrol->cmd = SOF_CTRL_CMD_VOLUME; + scontrol->control_data->cmd = SOF_CTRL_CMD_VOLUME; /* extract tlv data */ if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) { @@ -1148,7 +1165,7 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, scontrol->comp_id = sdev->next_comp_id; scontrol->num_channels = le32_to_cpu(ec->num_channels); scontrol->control_data->index = kc->index; - scontrol->cmd = SOF_CTRL_CMD_ENUM; + scontrol->control_data->cmd = SOF_CTRL_CMD_ENUM; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); @@ -1194,7 +1211,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, } scontrol->comp_id = sdev->next_comp_id; - scontrol->cmd = SOF_CTRL_CMD_BINARY; + scontrol->control_data->cmd = SOF_CTRL_CMD_BINARY; scontrol->control_data->index = kc->index; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", @@ -1329,69 +1346,6 @@ static int sof_control_unload(struct snd_soc_component *scomp, * DAI Topology */ -/* Static DSP core power management so far, should be extended in the future */ -static int sof_core_enable(struct snd_sof_dev *sdev, int core) -{ - struct sof_ipc_pm_core_config pm_core_config = { - .hdr = { - .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, - .size = sizeof(pm_core_config), - }, - .enable_mask = sdev->enabled_cores_mask | BIT(core), - }; - int ret; - - if (sdev->enabled_cores_mask & BIT(core)) - return 0; - - /* power up the core if it is host managed */ - ret = snd_sof_dsp_core_power_up(sdev, BIT(core)); - if (ret < 0) { - dev_err(sdev->dev, "error: %d powering up core %d\n", - ret, core); - return ret; - } - - /* Now notify DSP */ - ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, - &pm_core_config, sizeof(pm_core_config), - &pm_core_config, sizeof(pm_core_config)); - if (ret < 0) { - dev_err(sdev->dev, "error: core %d enable ipc failure %d\n", - core, ret); - goto err; - } - return ret; -err: - /* power down core if it is host managed and return the original error if this fails too */ - if (snd_sof_dsp_core_power_down(sdev, BIT(core)) < 0) - dev_err(sdev->dev, "error: powering down core %d\n", core); - - return ret; -} - -int sof_pipeline_core_enable(struct snd_sof_dev *sdev, - const struct snd_sof_widget *swidget) -{ - const struct sof_ipc_pipe_new *pipeline; - int ret; - - if (swidget->id == snd_soc_dapm_scheduler) { - pipeline = swidget->private; - } else { - pipeline = snd_sof_pipeline_find(sdev, swidget->pipeline_id); - if (!pipeline) - return -ENOENT; - } - - /* First enable the pipeline core */ - ret = sof_core_enable(sdev, pipeline->core); - if (ret < 0) - return ret; - - return sof_core_enable(sdev, swidget->core); -} - static int sof_connect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tw, @@ -1690,23 +1644,6 @@ err: /* * Pipeline Topology */ -int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, - struct sof_ipc_pipe_new *pipeline, - struct sof_ipc_comp_reply *r) -{ - int ret = sof_core_enable(sdev, pipeline->core); - - if (ret < 0) - return ret; - - ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, - sizeof(*pipeline), r, sizeof(*r)); - if (ret < 0) - dev_err(sdev->dev, "error: load pipeline ipc failure\n"); - - return ret; -} - static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw) @@ -1758,12 +1695,12 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, goto err; } - if (sof_core_debug & SOF_DBG_DISABLE_MULTICORE) + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) pipeline->core = SOF_DSP_PRIMARY_CORE; - if (sof_core_debug & SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE) - swidget->dynamic_pipeline_widget = sof_core_debug & - SOF_DBG_DYNAMIC_PIPELINES_ENABLE; + if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE)) + swidget->dynamic_pipeline_widget = + sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE); dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n", swidget->widget->name, pipeline->period, pipeline->priority, @@ -2139,7 +2076,7 @@ static int sof_get_control_data(struct snd_soc_component *scomp, *size += wdata[i].pdata->size; /* get data type */ - switch (wdata[i].control->cmd) { + switch (wdata[i].control->control_data->cmd) { case SOF_CTRL_CMD_VOLUME: case SOF_CTRL_CMD_ENUM: case SOF_CTRL_CMD_SWITCH: @@ -2358,7 +2295,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, return ret; } - if (sof_core_debug & SOF_DBG_DISABLE_MULTICORE) + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) comp.core = SOF_DSP_PRIMARY_CORE; swidget->core = comp.core; @@ -2485,10 +2422,8 @@ static int sof_route_unload(struct snd_soc_component *scomp, static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); const struct snd_kcontrol_new *kc; struct snd_soc_dapm_widget *widget; - struct sof_ipc_pipe_new *pipeline; struct snd_sof_control *scontrol; struct snd_sof_widget *swidget; struct soc_mixer_control *sm; @@ -2515,24 +2450,6 @@ static int sof_widget_unload(struct snd_soc_component *scomp, list_del(&dai->list); } break; - case snd_soc_dapm_scheduler: - - /* power down the pipeline schedule core */ - pipeline = swidget->private; - - /* - * Runtime PM should still function normally if topology loading fails and - * it's components are unloaded. Do not power down the primary core so that the - * CTX_SAVE IPC can succeed during runtime suspend. - */ - if (pipeline->core == SOF_DSP_PRIMARY_CORE) - break; - - ret = snd_sof_dsp_core_power_down(sdev, 1 << pipeline->core); - if (ret < 0) - dev_err(scomp->dev, "error: powering down pipeline schedule core %d\n", - pipeline->core); - break; default: break; } @@ -2992,6 +2909,144 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index, return ret; } +static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpdmic, 0, sizeof(struct sof_ipc_dai_acp_params)); + config->hdr.size = size; + + config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n", + config->dai_index, config->acpdmic.tdm_slots, + config->acpdmic.fsync_rate); + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "ACP_DMIC failed to save DAI config for ACP%d\n", + config->dai_index); + return ret; +} + +static int sof_link_acp_bt_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpbt, 0, sizeof(struct sof_ipc_dai_acp_params)); + config->hdr.size = size; + + config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n", + config->dai_index, config->acpbt.tdm_slots, + config->acpbt.fsync_rate); + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "ACP_BT failed to save DAI config for ACP%d\n", + config->dai_index); + return ret; +} + +static int sof_link_acp_sp_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpsp, 0, sizeof(struct sof_ipc_dai_acp_params)); + config->hdr.size = size; + + config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n", + config->dai_index, config->acpsp.tdm_slots, + config->acpsp.fsync_rate); + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "ACP_SP failed to save DAI config for ACP%d\n", + config->dai_index); + return ret; +} + +static int sof_link_afe_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct snd_soc_dai *dai; + u32 size = sizeof(*config); + int ret; + + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, &config->afe, afe_tokens, + ARRAY_SIZE(afe_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(scomp->dev, "parse afe tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n", + config->afe.rate, config->afe.channels, config->afe.format); + + dai = snd_soc_find_dai(link->cpus); + if (!dai) { + dev_err(scomp->dev, "%s: failed to find dai %s", __func__, link->cpus->dai_name); + return -EINVAL; + } + + config->afe.stream_id = DMA_CHAN_INVALID; + + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "failed to process afe dai link %s", link->name); + + return ret; +} + static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg, @@ -3277,6 +3332,19 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, case SOF_DAI_IMX_ESAI: ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config); break; + case SOF_DAI_AMD_BT: + ret = sof_link_acp_bt_load(scomp, index, link, cfg, hw_config + curr_conf, config); + break; + case SOF_DAI_AMD_SP: + ret = sof_link_acp_sp_load(scomp, index, link, cfg, hw_config + curr_conf, config); + break; + case SOF_DAI_AMD_DMIC: + ret = sof_link_acp_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, + config); + break; + case SOF_DAI_MEDIATEK_AFE: + ret = sof_link_afe_load(scomp, index, link, cfg, hw_config + curr_conf, config); + break; default: dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type); ret = -EINVAL; @@ -3461,7 +3529,7 @@ static int sof_complete(struct snd_soc_component *scomp) * Apply the dynamic_pipeline_widget flag and set the pipe_widget field * for all widgets that have the same pipeline ID as the scheduler widget */ - list_for_each_entry_reverse(comp_swidget, &sdev->widget_list, list) + list_for_each_entry(comp_swidget, &sdev->widget_list, list) if (comp_swidget->pipeline_id == swidget->pipeline_id) { ret = sof_set_pipe_widget(sdev, swidget, comp_swidget); if (ret < 0) @@ -3474,7 +3542,7 @@ static int sof_complete(struct snd_soc_component *scomp) } /* verify topology components loading including dynamic pipelines */ - if (sof_core_debug & SOF_DBG_VERIFY_TPLG) { + if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) { ret = sof_set_up_pipelines(sdev, true); if (ret < 0) { dev_err(sdev->dev, "error: topology verification failed %d\n", ret); diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index e3afc3dac7d1..f13024c8ebf2 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -539,6 +539,10 @@ EXPORT_SYMBOL(snd_sof_trace_notify_for_error); void snd_sof_release_trace(struct snd_sof_dev *sdev) { + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + struct sof_ipc_cmd_hdr hdr; + struct sof_ipc_reply ipc_reply; int ret; if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled) @@ -549,6 +553,20 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); + /* + * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from + * ABI 3.20.0 onwards + */ + if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) { + hdr.size = sizeof(hdr); + hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE; + + ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size, + &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) + dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret); + } + ret = snd_sof_dma_trace_release(sdev); if (ret < 0) dev_err(sdev->dev, diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c index bd09c3825caf..bebbe3a2865c 100644 --- a/sound/soc/sof/xtensa/core.c +++ b/sound/soc/sof/xtensa/core.c @@ -81,33 +81,39 @@ static const struct xtensa_exception_cause xtensa_exception_causes[] = { }; /* only need xtensa atm */ -static void xtensa_dsp_oops(struct snd_sof_dev *sdev, void *oops) +static void xtensa_dsp_oops(struct snd_sof_dev *sdev, const char *level, void *oops) { struct sof_ipc_dsp_oops_xtensa *xoops = oops; int i; - dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + dev_printk(level, sdev->dev, "error: DSP Firmware Oops\n"); for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { if (xtensa_exception_causes[i].id == xoops->exccause) { - dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", - xtensa_exception_causes[i].msg, - xtensa_exception_causes[i].description); + dev_printk(level, sdev->dev, + "error: Exception Cause: %s, %s\n", + xtensa_exception_causes[i].msg, + xtensa_exception_causes[i].description); } } - dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", - xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); - dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", - xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); - dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", - xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); - dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", - xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); - dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", - xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); + dev_printk(level, sdev->dev, + "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", + xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); + dev_printk(level, sdev->dev, + "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", + xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); + dev_printk(level, sdev->dev, + "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", + xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); + dev_printk(level, sdev->dev, + "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", + xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); + dev_printk(level, sdev->dev, + "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", + xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); } -static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, - u32 stack_words) +static void xtensa_stack(struct snd_sof_dev *sdev, const char *level, void *oops, + u32 *stack, u32 stack_words) { struct sof_ipc_dsp_oops_xtensa *xoops = oops; u32 stack_ptr = xoops->plat_hdr.stackptr; @@ -115,7 +121,7 @@ static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, unsigned char buf[4 * 8 + 3 + 1]; int i; - dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); + dev_printk(level, sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); /* * example output: @@ -124,7 +130,7 @@ static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, for (i = 0; i < stack_words; i += 4) { hex_dump_to_buffer(stack + i, 16, 16, 4, buf, sizeof(buf), false); - dev_err(sdev->dev, "0x%08x: %s\n", stack_ptr + i * 4, buf); + dev_printk(level, sdev->dev, "0x%08x: %s\n", stack_ptr + i * 4, buf); } } diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index e6078f50e508..6ee714542b84 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -12,7 +12,7 @@ #include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/slab.h> - +#include <linux/pm_runtime.h> #include <linux/iio/iio.h> #include <linux/iio/consumer.h> #include <linux/iio/adc/stm32-dfsdm-adc.h> @@ -334,6 +334,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, priv); + pm_runtime_enable(&pdev->dev); + ret = devm_snd_soc_register_component(&pdev->dev, &stm32_adfsdm_dai_component, &priv->dai_drv, 1); @@ -373,6 +375,7 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) static int stm32_adfsdm_remove(struct platform_device *pdev) { snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 717f45a83445..ac5dff4d1677 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/of_irq.h> #include <linux/of_platform.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/reset.h> #include <linux/spinlock.h> @@ -1044,36 +1045,24 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, /* Get clocks */ i2s->pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(i2s->pclk)) { - if (PTR_ERR(i2s->pclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get pclk: %ld\n", - PTR_ERR(i2s->pclk)); - return PTR_ERR(i2s->pclk); - } + if (IS_ERR(i2s->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->pclk), + "Could not get pclk\n"); i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk"); - if (IS_ERR(i2s->i2sclk)) { - if (PTR_ERR(i2s->i2sclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get i2sclk: %ld\n", - PTR_ERR(i2s->i2sclk)); - return PTR_ERR(i2s->i2sclk); - } + if (IS_ERR(i2s->i2sclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->i2sclk), + "Could not get i2sclk\n"); i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k"); - if (IS_ERR(i2s->x8kclk)) { - if (PTR_ERR(i2s->x8kclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get x8k parent clock: %ld\n", - PTR_ERR(i2s->x8kclk)); - return PTR_ERR(i2s->x8kclk); - } + if (IS_ERR(i2s->x8kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x8kclk), + "Could not get x8k parent clock\n"); i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k"); - if (IS_ERR(i2s->x11kclk)) { - if (PTR_ERR(i2s->x11kclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get x11k parent clock: %ld\n", - PTR_ERR(i2s->x11kclk)); - return PTR_ERR(i2s->x11kclk); - } + if (IS_ERR(i2s->x11kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x11kclk), + "Could not get x11k parent clock\n"); /* Register mclk provider if requested */ if (of_find_property(np, "#clock-cells", NULL)) { @@ -1096,12 +1085,10 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, /* Reset */ rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) { - if (PTR_ERR(rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Reset controller error %ld\n", - PTR_ERR(rst)); - return PTR_ERR(rst); - } + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Reset controller error\n"); + reset_control_assert(rst); udelay(2); reset_control_deassert(rst); @@ -1113,6 +1100,7 @@ static int stm32_i2s_remove(struct platform_device *pdev) { snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } @@ -1143,19 +1131,15 @@ static int stm32_i2s_probe(struct platform_device *pdev) i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk", i2s->base, i2s->regmap_conf); - if (IS_ERR(i2s->regmap)) { - if (PTR_ERR(i2s->regmap) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Regmap init error %ld\n", - PTR_ERR(i2s->regmap)); - return PTR_ERR(i2s->regmap); - } + if (IS_ERR(i2s->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), + "Regmap init error\n"); + + pm_runtime_enable(&pdev->dev); ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "PCM DMA register error %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, i2s->dai_drv, 1); diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 058757c721f0..8e21e6f886fc 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -173,29 +173,20 @@ static int stm32_sai_probe(struct platform_device *pdev) if (!STM_SAI_IS_F4(sai)) { sai->pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(sai->pclk)) { - if (PTR_ERR(sai->pclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "missing bus clock pclk: %ld\n", - PTR_ERR(sai->pclk)); - return PTR_ERR(sai->pclk); - } + if (IS_ERR(sai->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->pclk), + "missing bus clock pclk\n"); } sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); - if (IS_ERR(sai->clk_x8k)) { - if (PTR_ERR(sai->clk_x8k) != -EPROBE_DEFER) - dev_err(&pdev->dev, "missing x8k parent clock: %ld\n", - PTR_ERR(sai->clk_x8k)); - return PTR_ERR(sai->clk_x8k); - } + if (IS_ERR(sai->clk_x8k)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x8k), + "missing x8k parent clock\n"); sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k"); - if (IS_ERR(sai->clk_x11k)) { - if (PTR_ERR(sai->clk_x11k) != -EPROBE_DEFER) - dev_err(&pdev->dev, "missing x11k parent clock: %ld\n", - PTR_ERR(sai->clk_x11k)); - return PTR_ERR(sai->clk_x11k); - } + if (IS_ERR(sai->clk_x11k)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x11k), + "missing x11k parent clock\n"); /* init irqs */ sai->irq = platform_get_irq(pdev, 0); @@ -204,12 +195,10 @@ static int stm32_sai_probe(struct platform_device *pdev) /* reset */ rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) { - if (PTR_ERR(rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Reset controller error %ld\n", - PTR_ERR(rst)); - return PTR_ERR(rst); - } + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Reset controller error\n"); + reset_control_assert(rst); udelay(2); reset_control_deassert(rst); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 9c3b8e209656..dd636af81c9b 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1294,7 +1294,7 @@ static struct snd_soc_dai_driver stm32_sai_playback_dai = { .id = 1, /* avoid call to fmt_single_name() */ .playback = { .channels_min = 1, - .channels_max = 2, + .channels_max = 16, .rate_min = 8000, .rate_max = 192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, @@ -1312,7 +1312,7 @@ static struct snd_soc_dai_driver stm32_sai_capture_dai = { .id = 1, /* avoid call to fmt_single_name() */ .capture = { .channels_min = 1, - .channels_max = 2, + .channels_max = 16, .rate_min = 8000, .rate_max = 192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, @@ -1379,12 +1379,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, */ sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, sai->regmap_config); - if (IS_ERR(sai->regmap)) { - if (PTR_ERR(sai->regmap) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Regmap init error %ld\n", - PTR_ERR(sai->regmap)); - return PTR_ERR(sai->regmap); - } + if (IS_ERR(sai->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->regmap), + "Regmap init error\n"); /* Get direction property */ if (of_property_match_string(np, "dma-names", "tx") >= 0) { @@ -1472,12 +1469,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, of_node_put(args.np); sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); - if (IS_ERR(sai->sai_ck)) { - if (PTR_ERR(sai->sai_ck) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Missing kernel clock sai_ck: %ld\n", - PTR_ERR(sai->sai_ck)); - return PTR_ERR(sai->sai_ck); - } + if (IS_ERR(sai->sai_ck)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->sai_ck), + "Missing kernel clock sai_ck\n"); ret = clk_prepare(sai->pdata->pclk); if (ret < 0) @@ -1551,11 +1545,8 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) conf = &stm32_sai_pcm_config_spdif; ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not register pcm dma\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); ret = snd_soc_register_component(&pdev->dev, &stm32_component, &sai->cpu_dai_drv, 1); diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 48145f553588..6f7882c4fe6a 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -12,6 +12,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/of_platform.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/reset.h> @@ -405,12 +406,9 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, int ret; spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); - if (IS_ERR(spdifrx->ctrl_chan)) { - if (PTR_ERR(spdifrx->ctrl_chan) != -EPROBE_DEFER) - dev_err(dev, "dma_request_slave_channel error %ld\n", - PTR_ERR(spdifrx->ctrl_chan)); - return PTR_ERR(spdifrx->ctrl_chan); - } + if (IS_ERR(spdifrx->ctrl_chan)) + return dev_err_probe(dev, PTR_ERR(spdifrx->ctrl_chan), + "dma_request_slave_channel error\n"); spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer), GFP_KERNEL); @@ -929,12 +927,9 @@ static int stm32_spdifrx_parse_of(struct platform_device *pdev, spdifrx->phys_addr = res->start; spdifrx->kclk = devm_clk_get(&pdev->dev, "kclk"); - if (IS_ERR(spdifrx->kclk)) { - if (PTR_ERR(spdifrx->kclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get kclk: %ld\n", - PTR_ERR(spdifrx->kclk)); - return PTR_ERR(spdifrx->kclk); - } + if (IS_ERR(spdifrx->kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(spdifrx->kclk), + "Could not get kclk\n"); spdifrx->irq = platform_get_irq(pdev, 0); if (spdifrx->irq < 0) @@ -955,6 +950,7 @@ static int stm32_spdifrx_remove(struct platform_device *pdev) snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } @@ -985,12 +981,9 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) spdifrx->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "kclk", spdifrx->base, spdifrx->regmap_conf); - if (IS_ERR(spdifrx->regmap)) { - if (PTR_ERR(spdifrx->regmap) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Regmap init error %ld\n", - PTR_ERR(spdifrx->regmap)); - return PTR_ERR(spdifrx->regmap); - } + if (IS_ERR(spdifrx->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(spdifrx->regmap), + "Regmap init error\n"); ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, dev_name(&pdev->dev), spdifrx); @@ -1000,23 +993,20 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) } rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) { - if (PTR_ERR(rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Reset controller error %ld\n", - PTR_ERR(rst)); - return PTR_ERR(rst); - } + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Reset controller error\n"); + reset_control_assert(rst); udelay(2); reset_control_deassert(rst); + pm_runtime_enable(&pdev->dev); + pcm_config = &stm32_spdifrx_pcm_config; ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "PCM DMA register error %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); ret = snd_soc_register_component(&pdev->dev, &stm32_spdifrx_component, diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index da597e456beb..60712f24ade5 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1752,8 +1752,7 @@ static int sun4i_codec_probe(struct platform_device *pdev) GPIOD_OUT_LOW); if (IS_ERR(scodec->gpio_pa)) { ret = PTR_ERR(scodec->gpio_pa); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret); + dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); return ret; } diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index a10949bf0ca1..17090f43150e 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -21,6 +21,8 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/reset.h> +#include <linux/spinlock.h> +#include <sound/asoundef.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -186,6 +188,7 @@ struct sun4i_spdif_dev { struct regmap *regmap; struct snd_dmaengine_dai_dma_data dma_params_tx; const struct sun4i_spdif_quirks *quirks; + spinlock_t lock; }; static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) @@ -385,11 +388,122 @@ static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int sun4i_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int sun4i_spdif_get_status_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 *status = ucontrol->value.iec958.status; + + status[0] = 0xff; + status[1] = 0xff; + status[2] = 0xff; + status[3] = 0xff; + status[4] = 0xff; + status[5] = 0x03; + + return 0; +} + +static int sun4i_spdif_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + u8 *status = ucontrol->value.iec958.status; + unsigned long flags; + unsigned int reg; + + spin_lock_irqsave(&host->lock, flags); + + regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA0, ®); + + status[0] = reg & 0xff; + status[1] = (reg >> 8) & 0xff; + status[2] = (reg >> 16) & 0xff; + status[3] = (reg >> 24) & 0xff; + + regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA1, ®); + + status[4] = reg & 0xff; + status[5] = (reg >> 8) & 0x3; + + spin_unlock_irqrestore(&host->lock, flags); + + return 0; +} + +static int sun4i_spdif_set_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + u8 *status = ucontrol->value.iec958.status; + unsigned long flags; + unsigned int reg; + bool chg0, chg1; + + spin_lock_irqsave(&host->lock, flags); + + reg = (u32)status[3] << 24; + reg |= (u32)status[2] << 16; + reg |= (u32)status[1] << 8; + reg |= (u32)status[0]; + + regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA0, + GENMASK(31,0), reg, &chg0); + + reg = (u32)status[5] << 8; + reg |= (u32)status[4]; + + regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA1, + GENMASK(9,0), reg, &chg1); + + reg = SUN4I_SPDIF_TXCFG_CHSTMODE; + if (status[0] & IEC958_AES0_NONAUDIO) + reg |= SUN4I_SPDIF_TXCFG_NONAUDIO; + + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, + SUN4I_SPDIF_TXCFG_CHSTMODE | + SUN4I_SPDIF_TXCFG_NONAUDIO, reg); + + spin_unlock_irqrestore(&host->lock, flags); + + return chg0 || chg1; +} + +static struct snd_kcontrol_new sun4i_spdif_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = sun4i_spdif_info, + .get = sun4i_spdif_get_status_mask + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = sun4i_spdif_info, + .get = sun4i_spdif_get_status, + .put = sun4i_spdif_set_status + } +}; + static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) { struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL); + snd_soc_add_dai_controls(dai, sun4i_spdif_controls, + ARRAY_SIZE(sun4i_spdif_controls)); + return 0; } @@ -512,6 +626,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev) return -ENOMEM; host->pdev = pdev; + spin_lock_init(&host->lock); /* Initialize this copy of the CPU DAI driver structure */ memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai)); diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 518bfb724a5b..0bea2162f68d 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -21,6 +21,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <sound/tlv.h> #define SUN8I_SYSCLK_CTL 0x00c #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA 11 @@ -72,6 +73,12 @@ #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8 +#define SUN8I_AIF1_VOL_CTRL1 0x050 +#define SUN8I_AIF1_VOL_CTRL1_AD0L_VOL 8 +#define SUN8I_AIF1_VOL_CTRL1_AD0R_VOL 0 +#define SUN8I_AIF1_VOL_CTRL3 0x058 +#define SUN8I_AIF1_VOL_CTRL3_DA0L_VOL 8 +#define SUN8I_AIF1_VOL_CTRL3_DA0R_VOL 0 #define SUN8I_AIF2_ADCDAT_CTRL 0x084 #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_ENA 15 #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_ENA 14 @@ -91,6 +98,12 @@ #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA1R 10 #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF2DACL 9 #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_ADCR 8 +#define SUN8I_AIF2_VOL_CTRL1 0x090 +#define SUN8I_AIF2_VOL_CTRL1_ADCL_VOL 8 +#define SUN8I_AIF2_VOL_CTRL1_ADCR_VOL 0 +#define SUN8I_AIF2_VOL_CTRL2 0x098 +#define SUN8I_AIF2_VOL_CTRL2_DACL_VOL 8 +#define SUN8I_AIF2_VOL_CTRL2_DACR_VOL 0 #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1 (0x0 << 0) #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2 (0x1 << 0) #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1CLK (0x2 << 0) @@ -102,8 +115,14 @@ #define SUN8I_ADC_DIG_CTRL_ENAD 15 #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2 #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1 +#define SUN8I_ADC_VOL_CTRL 0x104 +#define SUN8I_ADC_VOL_CTRL_ADCL_VOL 8 +#define SUN8I_ADC_VOL_CTRL_ADCR_VOL 0 #define SUN8I_DAC_DIG_CTRL 0x120 #define SUN8I_DAC_DIG_CTRL_ENDA 15 +#define SUN8I_DAC_VOL_CTRL 0x124 +#define SUN8I_DAC_VOL_CTRL_DACL_VOL 8 +#define SUN8I_DAC_VOL_CTRL_DACR_VOL 0 #define SUN8I_DAC_MXR_SRC 0x130 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14 @@ -696,6 +715,41 @@ static struct snd_soc_dai_driver sun8i_codec_dais[] = { }, }; +static const DECLARE_TLV_DB_SCALE(sun8i_codec_vol_scale, -12000, 75, 1); + +static const struct snd_kcontrol_new sun8i_codec_controls[] = { + SOC_DOUBLE_TLV("AIF1 AD0 Capture Volume", + SUN8I_AIF1_VOL_CTRL1, + SUN8I_AIF1_VOL_CTRL1_AD0L_VOL, + SUN8I_AIF1_VOL_CTRL1_AD0R_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("AIF1 DA0 Playback Volume", + SUN8I_AIF1_VOL_CTRL3, + SUN8I_AIF1_VOL_CTRL3_DA0L_VOL, + SUN8I_AIF1_VOL_CTRL3_DA0R_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("AIF2 ADC Capture Volume", + SUN8I_AIF2_VOL_CTRL1, + SUN8I_AIF2_VOL_CTRL1_ADCL_VOL, + SUN8I_AIF2_VOL_CTRL1_ADCR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("AIF2 DAC Playback Volume", + SUN8I_AIF2_VOL_CTRL2, + SUN8I_AIF2_VOL_CTRL2_DACL_VOL, + SUN8I_AIF2_VOL_CTRL2_DACR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("ADC Capture Volume", + SUN8I_ADC_VOL_CTRL, + SUN8I_ADC_VOL_CTRL_ADCL_VOL, + SUN8I_ADC_VOL_CTRL_ADCR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("DAC Playback Volume", + SUN8I_DAC_VOL_CTRL, + SUN8I_DAC_VOL_CTRL_DACL_VOL, + SUN8I_DAC_VOL_CTRL_DACR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), +}; + static int sun8i_codec_aif_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1215,6 +1269,8 @@ static int sun8i_codec_component_probe(struct snd_soc_component *component) } static const struct snd_soc_component_driver sun8i_soc_component = { + .controls = sun8i_codec_controls, + .num_controls = ARRAY_SIZE(sun8i_codec_controls), .dapm_widgets = sun8i_codec_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets), .dapm_routes = sun8i_codec_dapm_routes, diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index 266d2cab9f49..27365a877e47 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -262,10 +262,59 @@ static int tegra20_i2s_probe(struct snd_soc_dai *dai) return 0; } +static const unsigned int tegra20_i2s_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000 +}; + +static int tegra20_i2s_filter_rates(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, rule->var); + struct snd_soc_dai *dai = rule->private; + struct tegra20_i2s *i2s = dev_get_drvdata(dai->dev); + struct clk *parent = clk_get_parent(i2s->clk_i2s); + long i, parent_rate, valid_rates = 0; + + parent_rate = clk_get_rate(parent); + if (parent_rate <= 0) { + dev_err(dai->dev, "Can't get parent clock rate: %ld\n", + parent_rate); + return parent_rate ?: -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(tegra20_i2s_rates); i++) { + if (parent_rate % (tegra20_i2s_rates[i] * 128) == 0) + valid_rates |= BIT(i); + } + + /* + * At least one rate must be valid, otherwise the parent clock isn't + * audio PLL. Nothing should be filtered in this case. + */ + if (!valid_rates) + valid_rates = BIT(ARRAY_SIZE(tegra20_i2s_rates)) - 1; + + return snd_interval_list(r, ARRAY_SIZE(tegra20_i2s_rates), + tegra20_i2s_rates, valid_rates); +} + +static int tegra20_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate")) + return 0; + + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + tegra20_i2s_filter_rates, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { .set_fmt = tegra20_i2s_set_fmt, .hw_params = tegra20_i2s_hw_params, .trigger = tegra20_i2s_trigger, + .startup = tegra20_i2s_startup, }; static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 7751575cd6d6..d09cd7ee6879 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -7,12 +7,15 @@ */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/reset.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -22,12 +25,12 @@ #include "tegra20_spdif.h" -#define DRV_NAME "tegra20-spdif" - static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev) { struct tegra20_spdif *spdif = dev_get_drvdata(dev); + regcache_cache_only(spdif->regmap, true); + clk_disable_unprepare(spdif->clk_spdif_out); return 0; @@ -38,23 +41,45 @@ static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev) struct tegra20_spdif *spdif = dev_get_drvdata(dev); int ret; + ret = reset_control_assert(spdif->reset); + if (ret) + return ret; + ret = clk_prepare_enable(spdif->clk_spdif_out); if (ret) { dev_err(dev, "clk_enable failed: %d\n", ret); return ret; } + usleep_range(10, 100); + + ret = reset_control_deassert(spdif->reset); + if (ret) + goto disable_clocks; + + regcache_cache_only(spdif->regmap, false); + regcache_mark_dirty(spdif->regmap); + + ret = regcache_sync(spdif->regmap); + if (ret) + goto disable_clocks; + return 0; + +disable_clocks: + clk_disable_unprepare(spdif->clk_spdif_out); + + return ret; } static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { - struct device *dev = dai->dev; - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); unsigned int mask = 0, val = 0; int ret, spdifclock; + long rate; mask |= TEGRA20_SPDIF_CTRL_PACK | TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; @@ -69,6 +94,14 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val); + /* + * FIFO trigger level must be bigger than DMA burst or equal to it, + * otherwise data is discarded on overflow. + */ + regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_DATA_FIFO_CSR, + TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK, + TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL); + switch (params_rate(params)) { case 32000: spdifclock = 4096000; @@ -97,10 +130,16 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); if (ret) { - dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); + dev_err(dai->dev, "Can't set SPDIF clock rate: %d\n", ret); return ret; } + rate = clk_get_rate(spdif->clk_spdif_out); + if (rate != spdifclock) + dev_warn_once(dai->dev, + "SPDIF clock rate %d doesn't match requested rate %lu\n", + spdifclock, rate); + return 0; } @@ -118,9 +157,9 @@ static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif) } static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -140,9 +179,62 @@ static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, rule->var); + struct snd_soc_dai *dai = rule->private; + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); + struct clk *parent = clk_get_parent(spdif->clk_spdif_out); + const unsigned int rates[] = { 32000, 44100, 48000 }; + long i, parent_rate, valid_rates = 0; + + parent_rate = clk_get_rate(parent); + if (parent_rate <= 0) { + dev_err(dai->dev, "Can't get parent clock rate: %ld\n", + parent_rate); + return parent_rate ?: -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(rates); i++) { + if (parent_rate % (rates[i] * 128) == 0) + valid_rates |= BIT(i); + } + + /* + * At least one rate must be valid, otherwise the parent clock isn't + * audio PLL. Nothing should be filtered in this case. + */ + if (!valid_rates) + valid_rates = BIT(ARRAY_SIZE(rates)) - 1; + + return snd_interval_list(r, ARRAY_SIZE(rates), rates, valid_rates); +} + +static int tegra20_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate")) + return 0; + + /* + * SPDIF and I2S share audio PLL. HDMI takes audio packets from SPDIF + * and audio may not work on some TVs if clock rate isn't precise. + * + * PLL rate is controlled by I2S side. Filter out audio rates that + * don't match PLL rate at the start of stream to allow both SPDIF + * and I2S work simultaneously, assuming that PLL rate won't be + * changed later on. + */ + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + tegra20_spdif_filter_rates, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static int tegra20_spdif_probe(struct snd_soc_dai *dai) { - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); dai->capture_dma_data = NULL; dai->playback_dma_data = &spdif->playback_dma_data; @@ -151,26 +243,27 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { - .hw_params = tegra20_spdif_hw_params, - .trigger = tegra20_spdif_trigger, + .hw_params = tegra20_spdif_hw_params, + .trigger = tegra20_spdif_trigger, + .startup = tegra20_spdif_startup, }; static struct snd_soc_dai_driver tegra20_spdif_dai = { - .name = DRV_NAME, + .name = "tegra20-spdif", .probe = tegra20_spdif_probe, .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, + SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &tegra20_spdif_dai_ops, }; static const struct snd_soc_component_driver tegra20_spdif_component = { - .name = DRV_NAME, + .name = "tegra20-spdif", }; static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) @@ -251,7 +344,7 @@ static const struct regmap_config tegra20_spdif_regmap_config = { static int tegra20_spdif_platform_probe(struct platform_device *pdev) { struct tegra20_spdif *spdif; - struct resource *mem, *dmareq; + struct resource *mem; void __iomem *regs; int ret; @@ -262,89 +355,77 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, spdif); - spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out"); + spdif->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(spdif->reset)) { + dev_err(&pdev->dev, "Can't retrieve spdif reset\n"); + return PTR_ERR(spdif->reset); + } + + spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out"); if (IS_ERR(spdif->clk_spdif_out)) { - pr_err("Can't retrieve spdif clock\n"); - ret = PTR_ERR(spdif->clk_spdif_out); - return ret; + dev_err(&pdev->dev, "Could not retrieve spdif clock\n"); + return PTR_ERR(spdif->clk_spdif_out); } regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(regs)) return PTR_ERR(regs); - dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmareq) { - dev_err(&pdev->dev, "No DMA resource\n"); - return -ENODEV; - } - spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &tegra20_spdif_regmap_config); + &tegra20_spdif_regmap_config); if (IS_ERR(spdif->regmap)) { dev_err(&pdev->dev, "regmap init failed\n"); - ret = PTR_ERR(spdif->regmap); - return ret; + return PTR_ERR(spdif->regmap); } spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; spdif->playback_dma_data.maxburst = 4; - spdif->playback_dma_data.slave_id = dmareq->start; - pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; - ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component, - &tegra20_spdif_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, + &tegra20_spdif_component, + &tegra20_spdif_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); - ret = -ENOMEM; - goto err_pm_disable; + return ret; } - ret = tegra_pcm_platform_register(&pdev->dev); + ret = devm_tegra_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); - goto err_unregister_component; + return ret; } return 0; - -err_unregister_component: - snd_soc_unregister_component(&pdev->dev); -err_pm_disable: - pm_runtime_disable(&pdev->dev); - - return ret; -} - -static int tegra20_spdif_platform_remove(struct platform_device *pdev) -{ - tegra_pcm_platform_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); - - pm_runtime_disable(&pdev->dev); - - return 0; } static const struct dev_pm_ops tegra20_spdif_pm_ops = { SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, tegra20_spdif_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; +static const struct of_device_id tegra20_spdif_of_match[] = { + { .compatible = "nvidia,tegra20-spdif", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra20_spdif_of_match); + static struct platform_driver tegra20_spdif_driver = { .driver = { - .name = DRV_NAME, + .name = "tegra20-spdif", .pm = &tegra20_spdif_pm_ops, + .of_match_table = tegra20_spdif_of_match, }, .probe = tegra20_spdif_platform_probe, - .remove = tegra20_spdif_platform_remove, }; - module_platform_driver(tegra20_spdif_driver); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h index 1973ffc2d5c7..ff4b79e2052f 100644 --- a/sound/soc/tegra/tegra20_spdif.h +++ b/sound/soc/tegra/tegra20_spdif.h @@ -451,6 +451,7 @@ struct tegra20_spdif { struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; struct regmap *regmap; + struct reset_control *reset; }; #endif diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c index acf59328dcb6..725385e17d84 100644 --- a/sound/soc/tegra/tegra210_mvc.c +++ b/sound/soc/tegra/tegra210_mvc.c @@ -108,67 +108,152 @@ static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val) } } -static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static u32 tegra210_mvc_get_ctrl_reg(struct snd_kcontrol *kcontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); - u8 mute_mask; u32 val; pm_runtime_get_sync(cmpnt->dev); regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &val); pm_runtime_put(cmpnt->dev); - mute_mask = (val >> TEGRA210_MVC_MUTE_SHIFT) & - TEGRA210_MUTE_MASK_EN; + return val; +} + +static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 val = tegra210_mvc_get_ctrl_reg(kcontrol); + u8 mute_mask = TEGRA210_GET_MUTE_VAL(val); - ucontrol->value.integer.value[0] = mute_mask; + /* + * If per channel control is enabled, then return + * exact mute/unmute setting of all channels. + * + * Else report setting based on CH0 bit to reflect + * the correct HW state. + */ + if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) { + ucontrol->value.integer.value[0] = mute_mask; + } else { + if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN) + ucontrol->value.integer.value[0] = + TEGRA210_MUTE_MASK_EN; + else + ucontrol->value.integer.value[0] = 0; + } return 0; } -static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int tegra210_mvc_get_master_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 val = tegra210_mvc_get_ctrl_reg(kcontrol); + u8 mute_mask = TEGRA210_GET_MUTE_VAL(val); + + /* + * If per channel control is disabled, then return + * master mute/unmute setting based on CH0 bit. + * + * Else report settings based on state of all + * channels. + */ + if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) { + ucontrol->value.integer.value[0] = + mute_mask & TEGRA210_MVC_CH0_MUTE_EN; + } else { + if (mute_mask == TEGRA210_MUTE_MASK_EN) + ucontrol->value.integer.value[0] = + TEGRA210_MVC_CH0_MUTE_EN; + else + ucontrol->value.integer.value[0] = 0; + } + + return 0; +} + +static int tegra210_mvc_volume_switch_timeout(struct snd_soc_component *cmpnt) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); - unsigned int value; - u8 new_mask, old_mask; + u32 value; int err; - pm_runtime_get_sync(cmpnt->dev); - - /* Check if VOLUME_SWITCH is triggered */ err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH, value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK), 10, 10000); if (err < 0) - goto end; + dev_err(cmpnt->dev, + "Volume switch trigger is still active, err = %d\n", + err); - regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &value); + return err; +} - old_mask = (value >> TEGRA210_MVC_MUTE_SHIFT) & TEGRA210_MUTE_MASK_EN; - new_mask = ucontrol->value.integer.value[0]; +static int tegra210_mvc_update_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + bool per_chan_ctrl) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); + u32 mute_val = ucontrol->value.integer.value[0]; + u32 per_ch_ctrl_val; + bool change = false; + int err; - if (new_mask == old_mask) { - err = 0; + pm_runtime_get_sync(cmpnt->dev); + + err = tegra210_mvc_volume_switch_timeout(cmpnt); + if (err < 0) goto end; + + if (per_chan_ctrl) { + per_ch_ctrl_val = TEGRA210_MVC_PER_CHAN_CTRL_EN; + } else { + per_ch_ctrl_val = 0; + + if (mute_val) + mute_val = TEGRA210_MUTE_MASK_EN; } - err = regmap_update_bits(mvc->regmap, mc->reg, + regmap_update_bits_check(mvc->regmap, TEGRA210_MVC_CTRL, TEGRA210_MVC_MUTE_MASK, - new_mask << TEGRA210_MVC_MUTE_SHIFT); - if (err < 0) - goto end; + mute_val << TEGRA210_MVC_MUTE_SHIFT, + &change); - err = 1; + if (change) { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, + per_ch_ctrl_val); + + regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, + TEGRA210_MVC_VOLUME_SWITCH_MASK, + TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); + } end: pm_runtime_put(cmpnt->dev); - return err; + + if (err < 0) + return err; + + if (change) + return 1; + + return 0; +} + +static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_update_mute(kcontrol, ucontrol, true); +} + +static int tegra210_mvc_put_master_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_update_mute(kcontrol, ucontrol, false); } static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, @@ -178,7 +263,7 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); - u8 chan = (mc->reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; + u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL); s32 val = mvc->volume[chan]; if (mvc->curve_type == CURVE_POLY) { @@ -193,44 +278,55 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, return 0; } -static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int tegra210_mvc_get_master_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_get_vol(kcontrol, ucontrol); +} + +static int tegra210_mvc_update_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + bool per_ch_enable) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); - unsigned int reg = mc->reg; - unsigned int value; - u8 chan; - int err, old_volume; + u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL); + int old_volume = mvc->volume[chan]; + int err, i; pm_runtime_get_sync(cmpnt->dev); - /* Check if VOLUME_SWITCH is triggered */ - err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH, - value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK), - 10, 10000); + err = tegra210_mvc_volume_switch_timeout(cmpnt); if (err < 0) goto end; - chan = (reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; - old_volume = mvc->volume[chan]; - - tegra210_mvc_conv_vol(mvc, chan, - ucontrol->value.integer.value[0]); + tegra210_mvc_conv_vol(mvc, chan, ucontrol->value.integer.value[0]); if (mvc->volume[chan] == old_volume) { err = 0; goto end; } + if (per_ch_enable) { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, + TEGRA210_MVC_PER_CHAN_CTRL_EN); + } else { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, 0); + + for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) + mvc->volume[i] = mvc->volume[chan]; + } + /* Configure init volume same as target volume */ regmap_write(mvc->regmap, TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan), mvc->volume[chan]); - regmap_write(mvc->regmap, reg, mvc->volume[chan]); + regmap_write(mvc->regmap, mc->reg, mvc->volume[chan]); regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, TEGRA210_MVC_VOLUME_SWITCH_MASK, @@ -240,9 +336,22 @@ static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, end: pm_runtime_put(cmpnt->dev); + return err; } +static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_update_vol(kcontrol, ucontrol, true); +} + +static int tegra210_mvc_put_master_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_update_vol(kcontrol, ucontrol, false); +} + static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc, struct device *dev) { @@ -436,6 +545,16 @@ static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = { TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0, tegra210_mvc_get_mute, tegra210_mvc_put_mute), + /* Master volume */ + SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0, + tegra210_mvc_get_master_vol, + tegra210_mvc_put_master_vol), + + /* Master mute */ + SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0, + tegra210_mvc_get_master_mute, + tegra210_mvc_put_master_mute), + SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl, tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type), }; diff --git a/sound/soc/tegra/tegra210_mvc.h b/sound/soc/tegra/tegra210_mvc.h index def29c4c7257..d775335dc60b 100644 --- a/sound/soc/tegra/tegra210_mvc.h +++ b/sound/soc/tegra/tegra210_mvc.h @@ -59,6 +59,7 @@ #define TEGRA210_MUTE_MASK_EN 0xff #define TEGRA210_MVC_MUTE_MASK (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT) #define TEGRA210_MVC_MUTE_EN (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT) +#define TEGRA210_MVC_CH0_MUTE_EN 1 #define TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT 30 #define TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT) @@ -92,6 +93,10 @@ #define TEGRA210_MVC_MAX_CHAN_COUNT 8 #define TEGRA210_MVC_REG_OFFSET(reg, i) (reg + (REG_SIZE * i)) +#define TEGRA210_MVC_GET_CHAN(reg, base) (((reg) - (base)) / REG_SIZE) + +#define TEGRA210_GET_MUTE_VAL(val) (((val) >> TEGRA210_MVC_MUTE_SHIFT) & TEGRA210_MUTE_MASK_EN) + #define NUM_GAIN_POLY_COEFFS 9 enum { diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index ef1e74d95236..468c8e77de21 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -48,6 +48,12 @@ int tegra_pcm_platform_register(struct device *dev) } EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); +int devm_tegra_pcm_platform_register(struct device *dev) +{ + return devm_snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0); +} +EXPORT_SYMBOL_GPL(devm_tegra_pcm_platform_register); + int tegra_pcm_platform_register_with_chan_names(struct device *dev, struct snd_dmaengine_pcm_config *config, char *txdmachan, char *rxdmachan) diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index d602126c65b7..2a36eea1740d 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -32,6 +32,7 @@ int tegra_pcm_hw_params(struct snd_soc_component *component, snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream); int tegra_pcm_platform_register(struct device *dev); +int devm_tegra_pcm_platform_register(struct device *dev); int tegra_pcm_platform_register_with_chan_names(struct device *dev, struct snd_dmaengine_pcm_config *config, char *txdmachan, char *rxdmachan); diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 56a19eeec5c7..2c146b91fca3 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1870,12 +1870,10 @@ err1: static bool davinci_mcasp_have_gpiochip(struct davinci_mcasp *mcasp) { #ifdef CONFIG_OF_GPIO - if (mcasp->dev->of_node && - of_property_read_bool(mcasp->dev->of_node, "gpio-controller")) - return true; -#endif - + return of_property_read_bool(mcasp->dev->of_node, "gpio-controller"); +#else return false; +#endif } static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, @@ -2026,13 +2024,9 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data; chan = dma_request_chan(mcasp->dev, tmp); - if (IS_ERR(chan)) { - if (PTR_ERR(chan) != -EPROBE_DEFER) - dev_err(mcasp->dev, - "Can't verify DMA configuration (%ld)\n", - PTR_ERR(chan)); - return PTR_ERR(chan); - } + if (IS_ERR(chan)) + return dev_err_probe(mcasp->dev, PTR_ERR(chan), + "Can't verify DMA configuration\n"); if (WARN_ON(!chan->device || !chan->device->dev)) { dma_release_channel(chan); return -EINVAL; @@ -2230,9 +2224,6 @@ static int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp) mcasp->gpio_chip = davinci_mcasp_template_chip; mcasp->gpio_chip.label = dev_name(mcasp->dev); mcasp->gpio_chip.parent = mcasp->dev; -#ifdef CONFIG_OF_GPIO - mcasp->gpio_chip.of_node = mcasp->dev->of_node; -#endif return devm_gpiochip_add_data(mcasp->dev, &mcasp->gpio_chip, mcasp); } diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index 9347f982c3e1..4077e15ec48b 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -464,13 +464,9 @@ static int j721e_get_clocks(struct device *dev, int ret; clocks->target = devm_clk_get(dev, prefix); - if (IS_ERR(clocks->target)) { - ret = PTR_ERR(clocks->target); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to acquire %s: %d\n", - prefix, ret); - return ret; - } + if (IS_ERR(clocks->target)) + return dev_err_probe(dev, PTR_ERR(clocks->target), + "failed to acquire %s\n", prefix); clk_name = kasprintf(GFP_KERNEL, "%s-48000", prefix); if (clk_name) { diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig index aa3592ee1358..ddfa6424c656 100644 --- a/sound/soc/uniphier/Kconfig +++ b/sound/soc/uniphier/Kconfig @@ -23,7 +23,6 @@ config SND_SOC_UNIPHIER_LD11 tristate "UniPhier LD11/LD20 Device Driver" depends on SND_SOC_UNIPHIER select SND_SOC_UNIPHIER_AIO - select SND_SOC_UNIPHIER_AIO_DMA help This adds ASoC driver for Socionext UniPhier LD11/LD20 input and output that can be used with other codecs. @@ -34,7 +33,6 @@ config SND_SOC_UNIPHIER_PXS2 tristate "UniPhier PXs2 Device Driver" depends on SND_SOC_UNIPHIER select SND_SOC_UNIPHIER_AIO - select SND_SOC_UNIPHIER_AIO_DMA help This adds ASoC driver for Socionext UniPhier PXs2 input and output that can be used with other codecs. diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c index e2ca087adee6..cba0e868a7d7 100644 --- a/sound/soc/xilinx/xlnx_spdif.c +++ b/sound/soc/xilinx/xlnx_spdif.c @@ -237,7 +237,6 @@ MODULE_DEVICE_TABLE(of, xlnx_spdif_of_match); static int xlnx_spdif_probe(struct platform_device *pdev) { int ret; - struct resource *res; struct snd_soc_dai_driver *dai_drv; struct spdif_dev_data *ctx; @@ -273,13 +272,10 @@ static int xlnx_spdif_probe(struct platform_device *pdev) if (ctx->mode) { dai_drv = &xlnx_spdif_tx_dai; } else { - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "No IRQ resource found\n"); - ret = -ENODEV; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto clk_err; - } - ret = devm_request_irq(dev, res->start, + ret = devm_request_irq(dev, ret, xlnx_spdifrx_irq_handler, 0, "XLNX_SPDIF_RX", ctx); if (ret) { diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 6b84f66e4af4..3881e1c1b08a 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -688,7 +688,7 @@ static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len) { u32 dvma_addr = (u32)dbri->dma_dvma; s32 tmp, addr; - static int wait_id = 0; + static int wait_id; wait_id++; wait_id &= 0xffff; /* restrict it to a 16 bit counter. */ @@ -1926,7 +1926,7 @@ static void dbri_process_interrupt_buffer(struct snd_dbri *dbri) static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id) { struct snd_dbri *dbri = dev_id; - static int errcnt = 0; + static int errcnt; int x; if (dbri == NULL) @@ -2591,7 +2591,7 @@ static int dbri_probe(struct platform_device *op) struct snd_dbri *dbri; struct resource *rp; struct snd_card *card; - static int dev = 0; + static int dev; int irq; int err; diff --git a/sound/usb/card.c b/sound/usb/card.c index 1764b9302d46..376962291c4d 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -987,8 +987,6 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip) wake_up(&chip->shutdown_wait); } -#ifdef CONFIG_PM - int snd_usb_autoresume(struct snd_usb_audio *chip) { int i, err; @@ -1100,11 +1098,6 @@ err_out: atomic_dec(&chip->active); /* allow autopm after this point */ return err; } -#else -#define usb_audio_suspend NULL -#define usb_audio_resume NULL -#define usb_audio_resume NULL -#endif /* CONFIG_PM */ static const struct usb_device_id usb_audio_ids [] = { #include "quirks-table.h" diff --git a/sound/usb/format.c b/sound/usb/format.c index f5e676a51b30..405dc0bf6678 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -375,7 +375,7 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, for (rate = min; rate <= max; rate += res) { /* Filter out invalid rates on Presonus Studio 1810c */ - if (chip->usb_id == USB_ID(0x0194f, 0x010c) && + if (chip->usb_id == USB_ID(0x194f, 0x010c) && !s1810c_valid_sample_rate(fp, rate)) goto skip_rate; diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 6e7bac8203ba..e8f3f8d622ec 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -145,6 +145,7 @@ static inline void check_mapped_dB(const struct usbmix_name_map *p, if (p && p->dB) { cval->dBmin = p->dB->min; cval->dBmax = p->dB->max; + cval->min_mute = p->dB->min_mute; cval->initialized = 1; } } @@ -3628,7 +3629,6 @@ void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer) mixer->disconnected = true; } -#ifdef CONFIG_PM /* stop any bus activity of a mixer */ static void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer) { @@ -3710,7 +3710,6 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer) return snd_usb_mixer_activate(mixer); } -#endif void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, struct usb_mixer_interface *mixer, @@ -3719,7 +3718,5 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, list->mixer = mixer; list->id = unitid; list->dump = snd_usb_mixer_dump_cval; -#ifdef CONFIG_PM list->resume = restore_mixer_value; -#endif } diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 98ea24d91d80..d43895c1ae5c 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -118,10 +118,8 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv); -#ifdef CONFIG_PM int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer); int snd_usb_mixer_resume(struct usb_mixer_interface *mixer); -#endif int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int value); diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 55eea90ee993..5d391f62351b 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -6,8 +6,9 @@ */ struct usbmix_dB_map { - u32 min; - u32 max; + int min; + int max; + bool min_mute; }; struct usbmix_name_map { @@ -336,6 +337,13 @@ static const struct usbmix_name_map bose_companion5_map[] = { { 0 } /* terminator */ }; +/* Bose Revolve+ SoundLink, correction of dB maps */ +static const struct usbmix_dB_map bose_soundlink_dB = {-8283, -0, true}; +static const struct usbmix_name_map bose_soundlink_map[] = { + { 2, NULL, .dB = &bose_soundlink_dB }, + { 0 } /* terminator */ +}; + /* Sennheiser Communications Headset [PC 8], the dB value is reported as -6 negative maximum */ static const struct usbmix_dB_map sennheiser_pc8_dB = {-9500, 0}; static const struct usbmix_name_map sennheiser_pc8_map[] = { @@ -522,6 +530,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = bose_companion5_map, }, { + /* Bose Revolve+ SoundLink */ + .id = USB_ID(0x05a7, 0x40fa), + .map = bose_soundlink_map, + }, + { /* Corsair Virtuoso SE (wired mode) */ .id = USB_ID(0x1b1c, 0x0a3d), .map = corsair_virtuoso_map, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 823b6b8de942..e447ddd6854c 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3254,7 +3254,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_rme_controls_create(mixer); break; - case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */ + case USB_ID(0x194f, 0x010c): /* Presonus Studio 1810c */ err = snd_sc1810_init_mixer(mixer); break; case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */ @@ -3280,7 +3280,6 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) return err; } -#ifdef CONFIG_PM void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer) { switch (mixer->chip->usb_id) { @@ -3289,7 +3288,6 @@ void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer) break; } } -#endif void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer, int unitid) diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h index 52be26db558f..4ba01ba3fe8b 100644 --- a/sound/usb/mixer_quirks.h +++ b/sound/usb/mixer_quirks.h @@ -14,9 +14,7 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, struct usb_mixer_elem_info *cval, int unitid, struct snd_kcontrol *kctl); -#ifdef CONFIG_PM void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer); -#endif #endif /* SND_USB_MIXER_QUIRKS_H */ diff --git a/sound/usb/power.h b/sound/usb/power.h index 6004231a7c75..396e3e51440a 100644 --- a/sound/usb/power.h +++ b/sound/usb/power.h @@ -21,17 +21,7 @@ struct snd_usb_power_domain * snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface, unsigned char id); -#ifdef CONFIG_PM int snd_usb_autoresume(struct snd_usb_audio *chip); void snd_usb_autosuspend(struct snd_usb_audio *chip); -#else -static inline int snd_usb_autoresume(struct snd_usb_audio *chip) -{ - return 0; -} -static inline void snd_usb_autosuspend(struct snd_usb_audio *chip) -{ -} -#endif #endif /* __USBAUDIO_POWER_H */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 64e1c20311ed..ab9f3da49941 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1290,7 +1290,7 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, if (chip->usb_id == USB_ID(0x0763, 0x2012)) return fasttrackpro_skip_setting_quirk(chip, iface, altno); /* presonus studio 1810c: skip altsets incompatible with device_setup */ - if (chip->usb_id == USB_ID(0x0194f, 0x010c)) + if (chip->usb_id == USB_ID(0x194f, 0x010c)) return s1810c_skip_setting_quirk(chip, iface, altno); diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 099bee662af6..52f4e6652407 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * usbusy2y.c - ALSA USB US-428 Driver + * usbusx2y.c - ALSA USB US-428 Driver * 2005-04-14 Karsten Wiese Version 0.8.7.2: diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 378826312abe..1c94eaff1931 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1750,7 +1750,9 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) card_ctx->irq = irq; /* only 32bit addressable */ - dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; init_channel_allocations(); diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index c852eb40c4f7..d08fe4cfe811 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -TARGETS = arm64 +TARGETS += alsa +TARGETS += arm64 TARGETS += bpf TARGETS += breakpoints TARGETS += capabilities diff --git a/tools/testing/selftests/alsa/.gitignore b/tools/testing/selftests/alsa/.gitignore new file mode 100644 index 000000000000..3bb7c41266a8 --- /dev/null +++ b/tools/testing/selftests/alsa/.gitignore @@ -0,0 +1 @@ +mixer-test diff --git a/tools/testing/selftests/alsa/Makefile b/tools/testing/selftests/alsa/Makefile new file mode 100644 index 000000000000..f64d9090426d --- /dev/null +++ b/tools/testing/selftests/alsa/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# + +CFLAGS += $(shell pkg-config --cflags alsa) +LDLIBS += $(shell pkg-config --libs alsa) + +TEST_GEN_PROGS := mixer-test + +include ../lib.mk diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c new file mode 100644 index 000000000000..17f158d7a767 --- /dev/null +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -0,0 +1,705 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// kselftest for the ALSA mixer API +// +// Original author: Mark Brown <broonie@kernel.org> +// Copyright (c) 2021 Arm Limited + +// This test will iterate over all cards detected in the system, exercising +// every mixer control it can find. This may conflict with other system +// software if there is audio activity so is best run on a system with a +// minimal active userspace. + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <getopt.h> +#include <stdarg.h> +#include <ctype.h> +#include <math.h> +#include <errno.h> +#include <assert.h> +#include <alsa/asoundlib.h> +#include <poll.h> +#include <stdint.h> + +#include "../kselftest.h" + +#define TESTS_PER_CONTROL 3 + +struct card_data { + snd_ctl_t *handle; + int card; + int num_ctls; + snd_ctl_elem_list_t *ctls; + struct card_data *next; +}; + +struct ctl_data { + const char *name; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + snd_ctl_elem_value_t *def_val; + int elem; + struct card_data *card; + struct ctl_data *next; +}; + +static const char *alsa_config = +"ctl.hw {\n" +" @args [ CARD ]\n" +" @args.CARD.type string\n" +" type hw\n" +" card $CARD\n" +"}\n" +; + +int num_cards = 0; +int num_controls = 0; +struct card_data *card_list = NULL; +struct ctl_data *ctl_list = NULL; + +#ifdef SND_LIB_VER +#if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6) +#define LIB_HAS_LOAD_STRING +#endif +#endif + +#ifndef LIB_HAS_LOAD_STRING +int snd_config_load_string(snd_config_t **config, const char *s, size_t size) +{ + snd_input_t *input; + snd_config_t *dst; + int err; + + assert(config && s); + if (size == 0) + size = strlen(s); + err = snd_input_buffer_open(&input, s, size); + if (err < 0) + return err; + err = snd_config_top(&dst); + if (err < 0) { + snd_input_close(input); + return err; + } + err = snd_config_load(dst, input); + snd_input_close(input); + if (err < 0) { + snd_config_delete(dst); + return err; + } + *config = dst; + return 0; +} +#endif + +void find_controls(void) +{ + char name[32]; + int card, ctl, err; + struct card_data *card_data; + struct ctl_data *ctl_data; + snd_config_t *config; + + card = -1; + if (snd_card_next(&card) < 0 || card < 0) + return; + + err = snd_config_load_string(&config, alsa_config, strlen(alsa_config)); + if (err < 0) { + ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n", + snd_strerror(err)); + ksft_exit_fail(); + } + + while (card >= 0) { + sprintf(name, "hw:%d", card); + + card_data = malloc(sizeof(*card_data)); + if (!card_data) + ksft_exit_fail_msg("Out of memory\n"); + + err = snd_ctl_open_lconf(&card_data->handle, name, 0, config); + if (err < 0) { + ksft_print_msg("Failed to get hctl for card %d: %s\n", + card, snd_strerror(err)); + goto next_card; + } + + /* Count controls */ + snd_ctl_elem_list_malloc(&card_data->ctls); + snd_ctl_elem_list(card_data->handle, card_data->ctls); + card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls); + + /* Enumerate control information */ + snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls); + snd_ctl_elem_list(card_data->handle, card_data->ctls); + + card_data->card = num_cards++; + card_data->next = card_list; + card_list = card_data; + + num_controls += card_data->num_ctls; + + for (ctl = 0; ctl < card_data->num_ctls; ctl++) { + ctl_data = malloc(sizeof(*ctl_data)); + if (!ctl_data) + ksft_exit_fail_msg("Out of memory\n"); + + ctl_data->card = card_data; + ctl_data->elem = ctl; + ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls, + ctl); + + err = snd_ctl_elem_id_malloc(&ctl_data->id); + if (err < 0) + ksft_exit_fail_msg("Out of memory\n"); + + err = snd_ctl_elem_info_malloc(&ctl_data->info); + if (err < 0) + ksft_exit_fail_msg("Out of memory\n"); + + err = snd_ctl_elem_value_malloc(&ctl_data->def_val); + if (err < 0) + ksft_exit_fail_msg("Out of memory\n"); + + snd_ctl_elem_list_get_id(card_data->ctls, ctl, + ctl_data->id); + snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id); + err = snd_ctl_elem_info(card_data->handle, + ctl_data->info); + if (err < 0) { + ksft_print_msg("%s getting info for %d\n", + snd_strerror(err), + ctl_data->name); + } + + snd_ctl_elem_value_set_id(ctl_data->def_val, + ctl_data->id); + + ctl_data->next = ctl_list; + ctl_list = ctl_data; + } + + next_card: + if (snd_card_next(&card) < 0) { + ksft_print_msg("snd_card_next"); + break; + } + } + + snd_config_delete(config); +} + +bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val, + int index) +{ + long int_val; + long long int64_val; + + switch (snd_ctl_elem_info_get_type(ctl->info)) { + case SND_CTL_ELEM_TYPE_NONE: + ksft_print_msg("%s.%d Invalid control type NONE\n", + ctl->name, index); + return false; + + case SND_CTL_ELEM_TYPE_BOOLEAN: + int_val = snd_ctl_elem_value_get_boolean(val, index); + switch (int_val) { + case 0: + case 1: + break; + default: + ksft_print_msg("%s.%d Invalid boolean value %ld\n", + ctl->name, index, int_val); + return false; + } + break; + + case SND_CTL_ELEM_TYPE_INTEGER: + int_val = snd_ctl_elem_value_get_integer(val, index); + + if (int_val < snd_ctl_elem_info_get_min(ctl->info)) { + ksft_print_msg("%s.%d value %ld less than minimum %ld\n", + ctl->name, index, int_val, + snd_ctl_elem_info_get_min(ctl->info)); + return false; + } + + if (int_val > snd_ctl_elem_info_get_max(ctl->info)) { + ksft_print_msg("%s.%d value %ld more than maximum %ld\n", + ctl->name, index, int_val, + snd_ctl_elem_info_get_max(ctl->info)); + return false; + } + + /* Only check step size if there is one and we're in bounds */ + if (snd_ctl_elem_info_get_step(ctl->info) && + (int_val - snd_ctl_elem_info_get_min(ctl->info) % + snd_ctl_elem_info_get_step(ctl->info))) { + ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n", + ctl->name, index, int_val, + snd_ctl_elem_info_get_step(ctl->info), + snd_ctl_elem_info_get_min(ctl->info)); + return false; + } + break; + + case SND_CTL_ELEM_TYPE_INTEGER64: + int64_val = snd_ctl_elem_value_get_integer64(val, index); + + if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) { + ksft_print_msg("%s.%d value %lld less than minimum %lld\n", + ctl->name, index, int64_val, + snd_ctl_elem_info_get_min64(ctl->info)); + return false; + } + + if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) { + ksft_print_msg("%s.%d value %lld more than maximum %lld\n", + ctl->name, index, int64_val, + snd_ctl_elem_info_get_max(ctl->info)); + return false; + } + + /* Only check step size if there is one and we're in bounds */ + if (snd_ctl_elem_info_get_step64(ctl->info) && + (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) % + snd_ctl_elem_info_get_step64(ctl->info)) { + ksft_print_msg("%s.%d value %lld invalid for step %lld minimum %lld\n", + ctl->name, index, int64_val, + snd_ctl_elem_info_get_step64(ctl->info), + snd_ctl_elem_info_get_min64(ctl->info)); + return false; + } + break; + + case SND_CTL_ELEM_TYPE_ENUMERATED: + int_val = snd_ctl_elem_value_get_enumerated(val, index); + + if (int_val < 0) { + ksft_print_msg("%s.%d negative value %ld for enumeration\n", + ctl->name, index, int_val); + return false; + } + + if (int_val >= snd_ctl_elem_info_get_items(ctl->info)) { + ksft_print_msg("%s.%d value %ld more than item count %ld\n", + ctl->name, index, int_val, + snd_ctl_elem_info_get_items(ctl->info)); + return false; + } + break; + + default: + /* No tests for other types */ + break; + } + + return true; +} + +/* + * Check that the provided value meets the constraints for the + * provided control. + */ +bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) +{ + int i; + bool valid = true; + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) + if (!ctl_value_index_valid(ctl, val, i)) + valid = false; + + return valid; +} + +/* + * Check that we can read the default value and it is valid. Write + * tests use the read value to restore the default. + */ +void test_ctl_get_value(struct ctl_data *ctl) +{ + int err; + + /* If the control is turned off let's be polite */ + if (snd_ctl_elem_info_is_inactive(ctl->info)) { + ksft_print_msg("%s is inactive\n", ctl->name); + ksft_test_result_skip("get_value.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + /* Can't test reading on an unreadable control */ + if (!snd_ctl_elem_info_is_readable(ctl->info)) { + ksft_print_msg("%s is not readable\n", ctl->name); + ksft_test_result_skip("get_value.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val); + if (err < 0) { + ksft_print_msg("snd_ctl_elem_read() failed: %s\n", + snd_strerror(err)); + goto out; + } + + if (!ctl_value_valid(ctl, ctl->def_val)) + err = -EINVAL; + +out: + ksft_test_result(err >= 0, "get_value.%d.%d\n", + ctl->card->card, ctl->elem); +} + +bool show_mismatch(struct ctl_data *ctl, int index, + snd_ctl_elem_value_t *read_val, + snd_ctl_elem_value_t *expected_val) +{ + long long expected_int, read_int; + + /* + * We factor out the code to compare values representable as + * integers, ensure that check doesn't log otherwise. + */ + expected_int = 0; + read_int = 0; + + switch (snd_ctl_elem_info_get_type(ctl->info)) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + expected_int = snd_ctl_elem_value_get_boolean(expected_val, + index); + read_int = snd_ctl_elem_value_get_boolean(read_val, index); + break; + + case SND_CTL_ELEM_TYPE_INTEGER: + expected_int = snd_ctl_elem_value_get_integer(expected_val, + index); + read_int = snd_ctl_elem_value_get_integer(read_val, index); + break; + + case SND_CTL_ELEM_TYPE_INTEGER64: + expected_int = snd_ctl_elem_value_get_integer64(expected_val, + index); + read_int = snd_ctl_elem_value_get_integer64(read_val, + index); + break; + + case SND_CTL_ELEM_TYPE_ENUMERATED: + expected_int = snd_ctl_elem_value_get_enumerated(expected_val, + index); + read_int = snd_ctl_elem_value_get_enumerated(read_val, + index); + break; + + default: + break; + } + + if (expected_int != read_int) { + /* + * NOTE: The volatile attribute means that the hardware + * can voluntarily change the state of control element + * independent of any operation by software. + */ + bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info); + ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n", + ctl->name, index, expected_int, read_int, is_volatile); + return !is_volatile; + } else { + return false; + } +} + +/* + * Write a value then if possible verify that we get the expected + * result. An optional expected value can be provided if we expect + * the write to fail, for verifying that invalid writes don't corrupt + * anything. + */ +int write_and_verify(struct ctl_data *ctl, + snd_ctl_elem_value_t *write_val, + snd_ctl_elem_value_t *expected_val) +{ + int err, i; + bool error_expected, mismatch_shown; + snd_ctl_elem_value_t *read_val, *w_val; + snd_ctl_elem_value_alloca(&read_val); + snd_ctl_elem_value_alloca(&w_val); + + /* + * We need to copy the write value since writing can modify + * the value which causes surprises, and allocate an expected + * value if we expect to read back what we wrote. + */ + snd_ctl_elem_value_copy(w_val, write_val); + if (expected_val) { + error_expected = true; + } else { + error_expected = false; + snd_ctl_elem_value_alloca(&expected_val); + snd_ctl_elem_value_copy(expected_val, write_val); + } + + /* + * Do the write, if we have an expected value ignore the error + * and carry on to validate the expected value. + */ + err = snd_ctl_elem_write(ctl->card->handle, w_val); + if (err < 0 && !error_expected) { + ksft_print_msg("snd_ctl_elem_write() failed: %s\n", + snd_strerror(err)); + return err; + } + + /* Can we do the verification part? */ + if (!snd_ctl_elem_info_is_readable(ctl->info)) + return err; + + snd_ctl_elem_value_set_id(read_val, ctl->id); + + err = snd_ctl_elem_read(ctl->card->handle, read_val); + if (err < 0) { + ksft_print_msg("snd_ctl_elem_read() failed: %s\n", + snd_strerror(err)); + return err; + } + + /* + * Use the libray to compare values, if there's a mismatch + * carry on and try to provide a more useful diagnostic than + * just "mismatch". + */ + if (!snd_ctl_elem_value_compare(expected_val, read_val)) + return 0; + + mismatch_shown = false; + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) + if (show_mismatch(ctl, i, read_val, expected_val)) + mismatch_shown = true; + + if (!mismatch_shown) + ksft_print_msg("%s read and written values differ\n", + ctl->name); + + return -1; +} + +/* + * Make sure we can write the default value back to the control, this + * should validate that at least some write works. + */ +void test_ctl_write_default(struct ctl_data *ctl) +{ + int err; + + /* If the control is turned off let's be polite */ + if (snd_ctl_elem_info_is_inactive(ctl->info)) { + ksft_print_msg("%s is inactive\n", ctl->name); + ksft_test_result_skip("write_default.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + if (!snd_ctl_elem_info_is_writable(ctl->info)) { + ksft_print_msg("%s is not writeable\n", ctl->name); + ksft_test_result_skip("write_default.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + /* No idea what the default was for unreadable controls */ + if (!snd_ctl_elem_info_is_readable(ctl->info)) { + ksft_print_msg("%s couldn't read default\n", ctl->name); + ksft_test_result_skip("write_default.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + err = write_and_verify(ctl, ctl->def_val, NULL); + + ksft_test_result(err >= 0, "write_default.%d.%d\n", + ctl->card->card, ctl->elem); +} + +bool test_ctl_write_valid_boolean(struct ctl_data *ctl) +{ + int err, i, j; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + for (j = 0; j < 2; j++) { + snd_ctl_elem_value_set_boolean(val, i, j); + err = write_and_verify(ctl, val, NULL); + if (err != 0) + fail = true; + } + } + + return !fail; +} + +bool test_ctl_write_valid_integer(struct ctl_data *ctl) +{ + int err; + int i; + long j, step; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + step = snd_ctl_elem_info_get_step(ctl->info); + if (!step) + step = 1; + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + for (j = snd_ctl_elem_info_get_min(ctl->info); + j <= snd_ctl_elem_info_get_max(ctl->info); j += step) { + + snd_ctl_elem_value_set_integer(val, i, j); + err = write_and_verify(ctl, val, NULL); + if (err != 0) + fail = true; + } + } + + + return !fail; +} + +bool test_ctl_write_valid_integer64(struct ctl_data *ctl) +{ + int err, i; + long long j, step; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + step = snd_ctl_elem_info_get_step64(ctl->info); + if (!step) + step = 1; + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + for (j = snd_ctl_elem_info_get_min64(ctl->info); + j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) { + + snd_ctl_elem_value_set_integer64(val, i, j); + err = write_and_verify(ctl, val, NULL); + if (err != 0) + fail = true; + } + } + + return !fail; +} + +bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) +{ + int err, i, j; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) { + snd_ctl_elem_value_set_enumerated(val, i, j); + err = write_and_verify(ctl, val, NULL); + if (err != 0) + fail = true; + } + } + + return !fail; +} + +void test_ctl_write_valid(struct ctl_data *ctl) +{ + bool pass; + int err; + + /* If the control is turned off let's be polite */ + if (snd_ctl_elem_info_is_inactive(ctl->info)) { + ksft_print_msg("%s is inactive\n", ctl->name); + ksft_test_result_skip("write_valid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + if (!snd_ctl_elem_info_is_writable(ctl->info)) { + ksft_print_msg("%s is not writeable\n", ctl->name); + ksft_test_result_skip("write_valid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + switch (snd_ctl_elem_info_get_type(ctl->info)) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + pass = test_ctl_write_valid_boolean(ctl); + break; + + case SND_CTL_ELEM_TYPE_INTEGER: + pass = test_ctl_write_valid_integer(ctl); + break; + + case SND_CTL_ELEM_TYPE_INTEGER64: + pass = test_ctl_write_valid_integer64(ctl); + break; + + case SND_CTL_ELEM_TYPE_ENUMERATED: + pass = test_ctl_write_valid_enumerated(ctl); + break; + + default: + /* No tests for this yet */ + ksft_test_result_skip("write_valid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + /* Restore the default value to minimise disruption */ + err = write_and_verify(ctl, ctl->def_val, NULL); + if (err < 0) + pass = false; + + ksft_test_result(pass, "write_valid.%d.%d\n", + ctl->card->card, ctl->elem); +} + +int main(void) +{ + struct ctl_data *ctl; + + ksft_print_header(); + + find_controls(); + + ksft_set_plan(num_controls * TESTS_PER_CONTROL); + + for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) { + /* + * Must test get_value() before we write anything, the + * test stores the default value for later cleanup. + */ + test_ctl_get_value(ctl); + test_ctl_write_default(ctl); + test_ctl_write_valid(ctl); + } + + ksft_exit_pass(); + + return 0; +} |