diff options
author | Linus Torvalds | 2020-04-02 15:50:04 -0700 |
---|---|---|
committer | Linus Torvalds | 2020-04-02 15:50:04 -0700 |
commit | 848960e576dafc8ed54c691b2f70b92e1fdea9ba (patch) | |
tree | 27ea80003da03b81f0b188d3712f0194745126d9 | |
parent | bc3b3f4bfbded031a11c4284106adddbfacd05bb (diff) | |
parent | 5c6cd7021a05a02fcf37f360592d7c18d4d807fb (diff) |
Merge tag 'sound-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"This became again a busy development cycle. There are few ALSA core
updates (merely API cleanups and sparse fixes), with the majority of
other changes are found in ASoC scene.
Here are some highlights:
ALSA core:
- More helper macros for sparse warning fixes (e.g. bitwise types)
- Slight optimization of PCM OSS locks
- Make common handling for PCM / compress buffers (for SOF)
ASoC:
- Lots of code refactoring and modernization for (still ongoing)
componentization works
- Conversion of SND_SOC_ALL_CODECS to use imply
- Continued refactoring and fixing of the Intel SOF/SST support,
including the initial (but still incomplete) SoundWire support
- SoundWire and more advanced clocking support for Realtek RT5682
- Support for amlogic GX, Meson 8, Meson 8B and T9015 DAC, Broadcom
DSL/PON, Ingenic JZ4760 and JZ4770, Realtek RL6231, and TI TAS2563
and TLV320ADCX140
HD-audio:
- Optimizations in HDMI jack handling
- A few new quirks and fixups for Realtek codecs
USB-audio:
- Delayed registration support
- New quirks for Motu, Kingston, Presonus"
* tag 'sound-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (415 commits)
ALSA: usb-audio: Fix case when USB MIDI interface has more than one extra endpoint descriptor
Revert "ALSA: uapi: Drop asound.h inclusion from asoc.h"
ALSA: hda/realtek - Remove now-unnecessary XPS 13 headphone noise fixups
ALSA: hda/realtek - Set principled PC Beep configuration for ALC256
ALSA: doc: Document PC Beep Hidden Register on Realtek ALC256
ALSA: hda/realtek - a fake key event is triggered by running shutup
ALSA: hda: default enable CA0132 DSP support
ASoC: amd: acp3x-pcm-dma: clean up two indentation issues
ASoC: tlv320adcx140: Remove undocumented property
ASoC: Intel: sof_sdw: Add Volteer support with RT5682 SNDW helper function
ASoC: Intel: common: add match table for TGL RT5682 SoundWire driver
ASoC: Intel: boards: add sof_sdw machine driver
ASoC: Intel: soc-acpi: update topology and driver name for SoundWire platforms
ASoC: rt5682: move DAI clock registry to I2S mode
ASoC: pxa: magician: convert to use i2c_new_client_device()
ASoC: SOF: Intel: hda-ctrl: add reset cycle before parsing capabilities
Asoc: SOF: Intel: hda: check SoundWire wakeen interrupt in irq thread
ASoC: SOF: Intel: hda: add WAKEEN interrupt support for SoundWire
ASoC: SOF: Intel: hda: add parameter to control SoundWire clock stop quirks
ASoC: SOF: Intel: hda: merge IPC, stream and SoundWire interrupt handlers
...
433 files changed, 19010 insertions, 4289 deletions
diff --git a/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml b/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml new file mode 100644 index 000000000000..a61bccf915d8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic AIU audio output controller + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + $nodename: + pattern: "^audio-controller@.*" + + "#sound-dai-cells": + const: 2 + + compatible: + items: + - enum: + - amlogic,aiu-gxbb + - amlogic,aiu-gxl + - amlogic,aiu-meson8 + - amlogic,aiu-meson8b + - const: + amlogic,aiu + + clocks: + items: + - description: AIU peripheral clock + - description: I2S peripheral clock + - description: I2S output clock + - description: I2S master clock + - description: I2S mixer clock + - description: SPDIF peripheral clock + - description: SPDIF output clock + - description: SPDIF master clock + - description: SPDIF master clock multiplexer + + clock-names: + items: + - const: pclk + - const: i2s_pclk + - const: i2s_aoclk + - const: i2s_mclk + - const: i2s_mixer + - const: spdif_pclk + - const: spdif_aoclk + - const: spdif_mclk + - const: spdif_mclk_sel + + interrupts: + items: + - description: I2S interrupt line + - description: SPDIF interrupt line + + interrupt-names: + items: + - const: i2s + - const: spdif + + reg: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - "#sound-dai-cells" + - compatible + - clocks + - clock-names + - interrupts + - interrupt-names + - reg + - resets + +examples: + - | + #include <dt-bindings/clock/gxbb-clkc.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/reset/amlogic,meson-gxbb-reset.h> + + aiu: audio-controller@5400 { + compatible = "amlogic,aiu-gxl", "amlogic,aiu"; + #sound-dai-cells = <2>; + reg = <0x0 0x5400 0x0 0x2ac>; + interrupts = <GIC_SPI 48 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 50 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "i2s", "spdif"; + clocks = <&clkc CLKID_AIU_GLUE>, + <&clkc CLKID_I2S_OUT>, + <&clkc CLKID_AOCLK_GATE>, + <&clkc CLKID_CTS_AMCLK>, + <&clkc CLKID_MIXER_IFACE>, + <&clkc CLKID_IEC958>, + <&clkc CLKID_IEC958_GATE>, + <&clkc CLKID_CTS_MCLK_I958>, + <&clkc CLKID_CTS_I958>; + clock-names = "pclk", + "i2s_pclk", + "i2s_aoclk", + "i2s_mclk", + "i2s_mixer", + "spdif_pclk", + "spdif_aoclk", + "spdif_mclk", + "spdif_mclk_sel"; + resets = <&reset RESET_AIU>; + }; + diff --git a/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml b/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml new file mode 100644 index 000000000000..f778d3371fde --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,g12a-toacodec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic G12a Internal DAC Control Glue + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + $nodename: + pattern: "^audio-controller@.*" + + "#sound-dai-cells": + const: 1 + + compatible: + oneOf: + - items: + - const: + amlogic,g12a-toacodec + - items: + - enum: + - amlogic,sm1-toacodec + - const: + amlogic,g12a-toacodec + + reg: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - "#sound-dai-cells" + - compatible + - reg + - resets + +examples: + - | + #include <dt-bindings/reset/amlogic,meson-g12a-audio-reset.h> + + toacodec: audio-controller@740 { + compatible = "amlogic,g12a-toacodec"; + reg = <0x0 0x740 0x0 0x4>; + #sound-dai-cells = <1>; + resets = <&clkc_audio AUD_RESET_TOACODEC>; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml new file mode 100644 index 000000000000..fb374c659be1 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,gx-sound-card.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic GX sound card + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + compatible: + items: + - const: amlogic,gx-sound-card + + audio-aux-devs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: list of auxiliary devices + + audio-routing: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + minItems: 2 + 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. + + audio-widgets: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + minItems: 2 + description: |- + A list off component DAPM widget. Each entry is a pair of strings, + the first being the widget type, the second being the widget name + + model: + $ref: /schemas/types.yaml#/definitions/string + description: User specified audio sound card name + +patternProperties: + "^dai-link-[0-9]+$": + type: object + description: |- + dai-link child nodes: + Container for dai-link level properties and the CODEC sub-nodes. + There should be at least one (and probably more) subnode of this type + + properties: + dai-format: + $ref: /schemas/types.yaml#/definitions/string + enum: [ i2s, left-j, dsp_a ] + + mclk-fs: + $ref: /schemas/types.yaml#/definitions/uint32 + description: |- + Multiplication factor between the frame rate and master clock + rate + + sound-dai: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle of the CPU DAI + + patternProperties: + "^codec-[0-9]+$": + type: object + description: |- + Codecs: + dai-link representing backend links should have at least one subnode. + One subnode for each codec of the dai-link. dai-link representing + frontend links have no codec, therefore have no subnodes + + properties: + sound-dai: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle of the codec DAI + + required: + - sound-dai + + required: + - sound-dai + +required: + - model + - dai-link-0 + +examples: + - | + sound { + compatible = "amlogic,gx-sound-card"; + model = "GXL-ACME-S905X-FOO"; + audio-aux-devs = <&>; + audio-routing = "I2S ENCODER I2S IN", "I2S FIFO Playback"; + + dai-link-0 { + sound-dai = <&i2s_fifo>; + }; + + dai-link-1 { + sound-dai = <&i2s_encoder>; + dai-format = "i2s"; + mclk-fs = <256>; + + codec-0 { + sound-dai = <&codec0>; + }; + + codec-1 { + sound-dai = <&codec1>; + }; + }; + }; + diff --git a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml new file mode 100644 index 000000000000..b7c38c2b5b54 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,t9015.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic T9015 Internal Audio DAC + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + $nodename: + pattern: "^audio-controller@.*" + + "#sound-dai-cells": + const: 0 + + compatible: + items: + - const: amlogic,t9015 + + clocks: + items: + - description: Peripheral clock + + clock-names: + items: + - const: pclk + + reg: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - "#sound-dai-cells" + - compatible + - reg + - clocks + - clock-names + - resets + +examples: + - | + #include <dt-bindings/clock/g12a-clkc.h> + #include <dt-bindings/reset/amlogic,meson-g12a-reset.h> + + acodec: audio-controller@32000 { + compatible = "amlogic,t9015"; + reg = <0x0 0x32000 0x0 0x14>; + #sound-dai-cells = <0>; + clocks = <&clkc CLKID_AUDIO_CODEC>; + clock-names = "pclk"; + resets = <&reset RESET_AUDIO_CODEC>; + }; + diff --git a/Documentation/devicetree/bindings/sound/brcm,bcm63xx-audio.txt b/Documentation/devicetree/bindings/sound/brcm,bcm63xx-audio.txt new file mode 100644 index 000000000000..007f524b4d15 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/brcm,bcm63xx-audio.txt @@ -0,0 +1,29 @@ +Broadcom DSL/PON BCM63xx Audio I2S controller + +Required properties: +- compatible: Should be "brcm,bcm63xx-i2s". +- #address-cells: 32bit valued, 1 cell. +- #size-cells: 32bit valued, 0 cell. +- reg: Should contain audio registers location and length +- interrupts: Should contain the interrupt for the controller. +- clocks: Must contain an entry for each entry in clock-names. + Please refer to clock-bindings.txt. +- clock-names: One of each entry matching the clocks phandles list: + - "i2sclk" (generated clock) Required. + - "i2sosc" (fixed 200MHz clock) Required. + +(1) : The generated clock is required only when any of TX and RX + works on Master Mode. +(2) : The fixed 200MHz clock is from internal chip and always on + +Example: + + i2s: bcm63xx-i2s { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm63xx-i2s"; + reg = <0xFF802080 0xFF>; + interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&i2sclk>, <&osc>; + clock-names = "i2sclk","i2sosc"; + }; diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l51.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l51.yaml new file mode 100644 index 000000000000..efce847a3408 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l51.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs42l51.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: CS42L51 audio codec DT bindings + +maintainers: + - Olivier Moysan <olivier.moysan@st.com> + +properties: + compatible: + const: cirrus,cs42l51 + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: MCLK + + reset-gpios: + maxItems: 1 + + VL-supply: + description: phandle to voltage regulator of digital interface section + + VD-supply: + description: phandle to voltage regulator of digital internal section + + VA-supply: + description: phandle to voltage regulator of analog internal section + + VAHP-supply: + description: phandle to voltage regulator of headphone + +required: + - compatible + - reg + - "#sound-dai-cells" + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + + cs42l51@4a { + compatible = "cirrus,cs42l51"; + reg = <0x4a>; + #sound-dai-cells = <0>; + clocks = <&mclk_prov>; + clock-names = "MCLK"; + VL-supply = <®_audio>; + VD-supply = <®_audio>; + VA-supply = <®_audio>; + VAHP-supply = <®_audio>; + reset-gpios = <&gpiog 9 GPIO_ACTIVE_LOW>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/sound/cs42l51.txt b/Documentation/devicetree/bindings/sound/cs42l51.txt deleted file mode 100644 index acbd68ddd2cb..000000000000 --- a/Documentation/devicetree/bindings/sound/cs42l51.txt +++ /dev/null @@ -1,33 +0,0 @@ -CS42L51 audio CODEC - -Required properties: - - - compatible : "cirrus,cs42l51" - - - reg : the I2C address of the device for I2C. - -Optional properties: - - VL-supply, VD-supply, VA-supply, VAHP-supply: power supplies for the device, - as covered in Documentation/devicetree/bindings/regulator/regulator.txt. - - - reset-gpios : GPIO specification for the reset pin. If specified, it will be - deasserted before starting the communication with the codec. - - - clocks : a list of phandles + clock-specifiers, one for each entry in - clock-names - - - clock-names : must contain "MCLK" - -Example: - -cs42l51: cs42l51@4a { - compatible = "cirrus,cs42l51"; - reg = <0x4a>; - clocks = <&mclk_prov>; - clock-names = "MCLK"; - VL-supply = <®_audio>; - VD-supply = <®_audio>; - VA-supply = <®_audio>; - VAHP-supply = <®_audio>; - reset-gpios = <&gpiog 9 GPIO_ACTIVE_LOW>; -}; diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt deleted file mode 100644 index 8ca52dcc5572..000000000000 --- a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt +++ /dev/null @@ -1,44 +0,0 @@ -Audio codec controlled by ChromeOS EC - -Google's ChromeOS EC codec is a digital mic codec provided by the -Embedded Controller (EC) and is controlled via a host-command interface. - -An EC codec node should only be found as a sub-node of the EC node (see -Documentation/devicetree/bindings/mfd/cros-ec.txt). - -Required properties: -- compatible: Must contain "google,cros-ec-codec" -- #sound-dai-cells: Should be 1. The cell specifies number of DAIs. - -Optional properties: -- reg: Pysical base address and length of shared memory region from EC. - It contains 3 unsigned 32-bit integer. The first 2 integers - combine to become an unsigned 64-bit physical address. The last - one integer is length of the shared memory. -- memory-region: Shared memory region to EC. A "shared-dma-pool". See - ../reserved-memory/reserved-memory.txt for details. - -Example: - -{ - ... - - reserved_mem: reserved_mem { - compatible = "shared-dma-pool"; - reg = <0 0x52800000 0 0x100000>; - no-map; - }; -} - -cros-ec@0 { - compatible = "google,cros-ec-spi"; - - ... - - cros_ec_codec: ec-codec { - compatible = "google,cros-ec-codec"; - #sound-dai-cells = <1>; - reg = <0x0 0x10500000 0x80000>; - memory-region = <&reserved_mem>; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml new file mode 100644 index 000000000000..c84e656afb0a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/google,cros-ec-codec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Audio codec controlled by ChromeOS EC + +maintainers: + - Cheng-Yi Chiang <cychiang@chromium.org> + +description: | + Google's ChromeOS EC codec is a digital mic codec provided by the + Embedded Controller (EC) and is controlled via a host-command interface. + An EC codec node should only be found as a sub-node of the EC node (see + Documentation/devicetree/bindings/mfd/cros-ec.txt). + +properties: + compatible: + const: google,cros-ec-codec + + "#sound-dai-cells": + const: 1 + + reg: + items: + - description: | + Physical base address and length of shared memory region from EC. + It contains 3 unsigned 32-bit integer. The first 2 integers + combine to become an unsigned 64-bit physical address. + The last one integer is the length of the shared memory. + + memory-region: + $ref: '/schemas/types.yaml#/definitions/phandle' + description: | + Shared memory region to EC. A "shared-dma-pool". + See ../reserved-memory/reserved-memory.txt for details. + +required: + - compatible + - '#sound-dai-cells' + +additionalProperties: false + +examples: + - | + reserved_mem: reserved-mem@52800000 { + compatible = "shared-dma-pool"; + reg = <0x52800000 0x100000>; + no-map; + }; + spi { + #address-cells = <1>; + #size-cells = <0>; + cros-ec@0 { + compatible = "google,cros-ec-spi"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0>; + cros_ec_codec: ec-codec@10500000 { + compatible = "google,cros-ec-codec"; + #sound-dai-cells = <1>; + reg = <0x0 0x10500000 0x80000>; + memory-region = <&reserved_mem>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/ingenic,aic.yaml b/Documentation/devicetree/bindings/sound/ingenic,aic.yaml new file mode 100644 index 000000000000..44f49bebb267 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ingenic,aic.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ingenic,aic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ingenic SoCs AC97 / I2S Controller (AIC) DT bindings + +maintainers: + - Paul Cercueil <paul@crapouillou.net> + +properties: + $nodename: + pattern: '^audio-controller@' + + compatible: + oneOf: + - enum: + - ingenic,jz4740-i2s + - ingenic,jz4760-i2s + - ingenic,jz4770-i2s + - ingenic,jz4780-i2s + - items: + - const: ingenic,jz4725b-i2s + - const: ingenic,jz4740-i2s + + '#sound-dai-cells': + const: 0 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: AIC clock + - description: I2S clock + - description: EXT clock + - description: PLL/2 clock + + clock-names: + items: + - const: aic + - const: i2s + - const: ext + - const: pll half + + dmas: + items: + - description: DMA controller phandle and request line for I2S RX + - description: DMA controller phandle and request line for I2S TX + + dma-names: + items: + - const: rx + - const: tx + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - dmas + - dma-names + - '#sound-dai-cells' + +examples: + - | + #include <dt-bindings/clock/jz4740-cgu.h> + aic: audio-controller@10020000 { + compatible = "ingenic,jz4740-i2s"; + reg = <0x10020000 0x38>; + + #sound-dai-cells = <0>; + + interrupt-parent = <&intc>; + interrupts = <18>; + + clocks = <&cgu JZ4740_CLK_AIC>, + <&cgu JZ4740_CLK_I2S>, + <&cgu JZ4740_CLK_EXT>, + <&cgu JZ4740_CLK_PLL_HALF>; + clock-names = "aic", "i2s", "ext", "pll half"; + + dmas = <&dmac 25 0xffffffff>, <&dmac 24 0xffffffff>; + dma-names = "rx", "tx"; + }; diff --git a/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt b/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt deleted file mode 100644 index b623d50004fb..000000000000 --- a/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt +++ /dev/null @@ -1,23 +0,0 @@ -Ingenic JZ4740 I2S controller - -Required properties: -- compatible : "ingenic,jz4740-i2s" or "ingenic,jz4780-i2s" -- reg : I2S registers location and length -- clocks : AIC and I2S PLL clock specifiers. -- clock-names: "aic" and "i2s" -- dmas: DMA controller phandle and DMA request line for I2S Tx and Rx channels -- dma-names: Must be "tx" and "rx" - -Example: - -i2s: i2s@10020000 { - compatible = "ingenic,jz4740-i2s"; - reg = <0x10020000 0x94>; - - clocks = <&cgu JZ4740_CLK_AIC>, <&cgu JZ4740_CLK_I2SPLL>; - clock-names = "aic", "i2s"; - - dmas = <&dma 2>, <&dma 3>; - dma-names = "tx", "rx"; - -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt index b795d282818d..a8f2b0c56c79 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt @@ -18,6 +18,7 @@ Required properties: * 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 diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt index 2469588c7ccb..1ecd75d2032a 100644 --- a/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt +++ b/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt @@ -10,6 +10,11 @@ Required properties: - clock-names: should be "pclk". - spk-depop-time-ms: speak depop time msec. +Optional properties: + +- mute-gpios: GPIO specifier for external line driver control (typically the + dedicated GPIO_MUTE pin) + Example for rk3328 internal codec: codec: codec@ff410000 { @@ -18,6 +23,6 @@ codec: codec@ff410000 { rockchip,grf = <&grf>; clocks = <&cru PCLK_ACODEC>; clock-names = "pclk"; + mute-gpios = <&grf_gpio 0 GPIO_ACTIVE_LOW>; spk-depop-time-ms = 100; - status = "disabled"; }; diff --git a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt deleted file mode 100644 index 54aefab71f2c..000000000000 --- a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt +++ /dev/null @@ -1,49 +0,0 @@ -* Rockchip I2S controller - -The I2S bus (Inter-IC sound bus) is a serial link for digital -audio data transfer between devices in the system. - -Required properties: - -- compatible: should be one of the following: - - "rockchip,rk3066-i2s": for rk3066 - - "rockchip,px30-i2s", "rockchip,rk3066-i2s": for px30 - - "rockchip,rk3036-i2s", "rockchip,rk3066-i2s": for rk3036 - - "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188 - - "rockchip,rk3228-i2s", "rockchip,rk3066-i2s": for rk3228 - - "rockchip,rk3288-i2s", "rockchip,rk3066-i2s": for rk3288 - - "rockchip,rk3328-i2s", "rockchip,rk3066-i2s": for rk3328 - - "rockchip,rk3366-i2s", "rockchip,rk3066-i2s": for rk3366 - - "rockchip,rk3368-i2s", "rockchip,rk3066-i2s": for rk3368 - - "rockchip,rk3399-i2s", "rockchip,rk3066-i2s": for rk3399 -- reg: physical base address of the controller and length of memory mapped - region. -- interrupts: should contain the I2S interrupt. -- dmas: DMA specifiers for tx and rx dma. See the DMA client binding, - Documentation/devicetree/bindings/dma/dma.txt -- dma-names: should include "tx" and "rx". -- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names. -- clock-names: should contain the following: - - "i2s_hclk": clock for I2S BUS - - "i2s_clk" : clock for I2S controller -- rockchip,playback-channels: max playback channels, if not set, 8 channels default. -- rockchip,capture-channels: max capture channels, if not set, 2 channels default. - -Required properties for controller which support multi channels -playback/capture: - -- rockchip,grf: the phandle of the syscon node for GRF register. - -Example for rk3288 I2S controller: - -i2s@ff890000 { - compatible = "rockchip,rk3288-i2s", "rockchip,rk3066-i2s"; - reg = <0xff890000 0x10000>; - interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>; - dmas = <&pdma1 0>, <&pdma1 1>; - dma-names = "tx", "rx"; - clock-names = "i2s_hclk", "i2s_clk"; - clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>; - rockchip,playback-channels = <8>; - rockchip,capture-channels = <2>; -}; diff --git a/Documentation/devicetree/bindings/sound/rockchip-i2s.yaml b/Documentation/devicetree/bindings/sound/rockchip-i2s.yaml new file mode 100644 index 000000000000..7cd0e278ed85 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip-i2s.yaml @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/rockchip-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip I2S controller + +description: + The I2S bus (Inter-IC sound bus) is a serial link for digital + audio data transfer between devices in the system. + +maintainers: + - Heiko Stuebner <heiko@sntech.de> + +properties: + compatible: + oneOf: + - const: rockchip,rk3066-i2s + - items: + - enum: + - rockchip,px30-i2s + - rockchip,rk3036-i2s + - rockchip,rk3188-i2s + - rockchip,rk3228-i2s + - rockchip,rk3288-i2s + - rockchip,rk3328-i2s + - rockchip,rk3366-i2s + - rockchip,rk3368-i2s + - rockchip,rk3399-i2s + - const: rockchip,rk3066-i2s + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: clock for I2S controller + - description: clock for I2S BUS + + clock-names: + items: + - const: i2s_clk + - const: i2s_hclk + + dmas: + items: + - description: TX DMA Channel + - description: RX DMA Channel + + dma-names: + items: + - const: tx + - const: rx + + rockchip,capture-channels: + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + default: 2 + description: + Max capture channels, if not set, 2 channels default. + + rockchip,playback-channels: + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + default: 8 + description: + Max playback channels, if not set, 8 channels default. + + rockchip,grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + The phandle of the syscon node for the GRF register. + Required property for controllers which support multi channel + playback/capture. + + "#sound-dai-cells": + const: 0 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - dmas + - dma-names + - "#sound-dai-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/rk3288-cru.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + i2s@ff890000 { + compatible = "rockchip,rk3288-i2s", "rockchip,rk3066-i2s"; + reg = <0xff890000 0x10000>; + interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cru SCLK_I2S0>, <&cru HCLK_I2S0>; + clock-names = "i2s_clk", "i2s_hclk"; + dmas = <&pdma1 0>, <&pdma1 1>; + dma-names = "tx", "rx"; + rockchip,capture-channels = <2>; + rockchip,playback-channels = <8>; + #sound-dai-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/sound/rt5682.txt b/Documentation/devicetree/bindings/sound/rt5682.txt index 30e927a28369..ade1ece8b45f 100644 --- a/Documentation/devicetree/bindings/sound/rt5682.txt +++ b/Documentation/devicetree/bindings/sound/rt5682.txt @@ -32,6 +32,18 @@ Optional properties: The delay time is realtek,btndet-delay value multiple of 8.192 ms. If absent, the default is 16. +- #clock-cells : Should be set to '<1>', wclk and bclk sources provided. +- clock-output-names : Name given for DAI clocks output. + +- clocks : phandle and clock specifier for codec MCLK. +- clock-names : Clock name string for 'clocks' attribute, should be "mclk". + +- realtek,dmic-clk-rate-hz : Set the clock rate (hz) for the requirement of + the particular DMIC. + +- realtek,dmic-delay-ms : Set the delay time (ms) for the requirement of + the particular DMIC. + Pins on the device (for linking into audio routes) for RT5682: * DMIC L1 @@ -53,4 +65,10 @@ rt5682 { realtek,dmic1-clk-pin = <1>; realtek,jd-src = <1>; realtek,btndet-delay = <16>; + + #clock-cells = <1>; + clock-output-names = "rt5682-dai-wclk", "rt5682-dai-bclk"; + + clocks = <&osc>; + clock-names = "mclk"; }; diff --git a/Documentation/devicetree/bindings/sound/st,stm32-i2s.txt b/Documentation/devicetree/bindings/sound/st,stm32-i2s.txt deleted file mode 100644 index cbf24bcd1b8d..000000000000 --- a/Documentation/devicetree/bindings/sound/st,stm32-i2s.txt +++ /dev/null @@ -1,62 +0,0 @@ -STMicroelectronics STM32 SPI/I2S Controller - -The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode. -Only some SPI instances support I2S. - -Required properties: - - compatible: Must be "st,stm32h7-i2s" - - reg: Offset and length of the device's register set. - - interrupts: Must contain the interrupt line id. - - clocks: Must contain phandle and clock specifier pairs for each entry - in clock-names. - - clock-names: Must contain "i2sclk", "pclk", "x8k" and "x11k". - "i2sclk": clock which feeds the internal clock generator - "pclk": clock which feeds the peripheral bus interface - "x8k": I2S parent clock for sampling rates multiple of 8kHz. - "x11k": I2S parent clock for sampling rates multiple of 11.025kHz. - - dmas: DMA specifiers for tx and rx dma. - See Documentation/devicetree/bindings/dma/stm32-dma.txt. - - dma-names: Identifier for each DMA request line. Must be "tx" and "rx". - - pinctrl-names: should contain only value "default" - - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml - -Optional properties: - - resets: Reference to a reset controller asserting the reset controller - -The device node should contain one 'port' child node with one child 'endpoint' -node, according to the bindings defined in Documentation/devicetree/bindings/ -graph.txt. - -Example: -sound_card { - compatible = "audio-graph-card"; - dais = <&i2s2_port>; -}; - -i2s2: audio-controller@40003800 { - compatible = "st,stm32h7-i2s"; - reg = <0x40003800 0x400>; - interrupts = <36>; - clocks = <&rcc PCLK1>, <&rcc SPI2_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>; - clock-names = "pclk", "i2sclk", "x8k", "x11k"; - dmas = <&dmamux2 2 39 0x400 0x1>, - <&dmamux2 3 40 0x400 0x1>; - dma-names = "rx", "tx"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2s2>; - - i2s2_port: port@0 { - cpu_endpoint: endpoint { - remote-endpoint = <&codec_endpoint>; - format = "i2s"; - }; - }; -}; - -audio-codec { - codec_port: port@0 { - codec_endpoint: endpoint { - remote-endpoint = <&cpu_endpoint>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml b/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml new file mode 100644 index 000000000000..f32410890589 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/st,stm32-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STM32 SPI/I2S Controller + +maintainers: + - Olivier Moysan <olivier.moysan@st.com> + +description: + The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode. + Only some SPI instances support I2S. + +properties: + compatible: + enum: + - st,stm32h7-i2s + + "#sound-dai-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + items: + - description: clock feeding the peripheral bus interface. + - description: clock feeding the internal clock generator. + - description: I2S parent clock for sampling rates multiple of 8kHz. + - description: I2S parent clock for sampling rates multiple of 11.025kHz. + + clock-names: + items: + - const: pclk + - const: i2sclk + - const: x8k + - const: x11k + + interrupts: + maxItems: 1 + + dmas: + items: + - description: audio capture DMA. + - description: audio playback DMA. + + dma-names: + items: + - const: rx + - const: tx + + resets: + maxItems: 1 + +required: + - compatible + - "#sound-dai-cells" + - reg + - clocks + - clock-names + - interrupts + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/stm32mp1-clks.h> + i2s2: audio-controller@4000b000 { + compatible = "st,stm32h7-i2s"; + #sound-dai-cells = <0>; + reg = <0x4000b000 0x400>; + clocks = <&rcc SPI2>, <&rcc SPI2_K>, <&rcc PLL3_Q>, <&rcc PLL3_R>; + clock-names = "pclk", "i2sclk", "x8k", "x11k"; + interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmamux1 39 0x400 0x01>, + <&dmamux1 40 0x400 0x01>; + dma-names = "rx", "tx"; + pinctrl-names = "default"; + pinctrl-0 = <&i2s2_pins_a>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt b/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt deleted file mode 100644 index ca9101777c44..000000000000 --- a/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt +++ /dev/null @@ -1,56 +0,0 @@ -STMicroelectronics STM32 S/PDIF receiver (SPDIFRX). - -The SPDIFRX peripheral, is designed to receive an S/PDIF flow compliant with -IEC-60958 and IEC-61937. - -Required properties: - - compatible: should be "st,stm32h7-spdifrx" - - reg: cpu DAI IP base address and size - - clocks: must contain an entry for kclk (used as S/PDIF signal reference) - - clock-names: must contain "kclk" - - interrupts: cpu DAI interrupt line - - dmas: DMA specifiers for audio data DMA and iec control flow DMA - See STM32 DMA bindings, Documentation/devicetree/bindings/dma/st,stm32-dma.yaml - - dma-names: two dmas have to be defined, "rx" and "rx-ctrl" - -Optional properties: - - resets: Reference to a reset controller asserting the SPDIFRX - -The device node should contain one 'port' child node with one child 'endpoint' -node, according to the bindings defined in Documentation/devicetree/bindings/ -graph.txt. - -Example: -spdifrx: spdifrx@40004000 { - compatible = "st,stm32h7-spdifrx"; - reg = <0x40004000 0x400>; - clocks = <&rcc SPDIFRX_CK>; - clock-names = "kclk"; - interrupts = <97>; - dmas = <&dmamux1 2 93 0x400 0x0>, - <&dmamux1 3 94 0x400 0x0>; - dma-names = "rx", "rx-ctrl"; - pinctrl-0 = <&spdifrx_pins>; - pinctrl-names = "default"; - - spdifrx_port: port { - cpu_endpoint: endpoint { - remote-endpoint = <&codec_endpoint>; - }; - }; -}; - -spdif_in: spdif-in { - compatible = "linux,spdif-dir"; - - codec_port: port { - codec_endpoint: endpoint { - remote-endpoint = <&cpu_endpoint>; - }; - }; -}; - -soundcard { - compatible = "audio-graph-card"; - dais = <&spdifrx_port>; -}; diff --git a/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.yaml b/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.yaml new file mode 100644 index 000000000000..b7f7dc452231 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/st,stm32-spdifrx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STM32 S/PDIF receiver (SPDIFRX) + +maintainers: + - Olivier Moysan <olivier.moysan@st.com> + +description: | + The SPDIFRX peripheral, is designed to receive an S/PDIF flow compliant with + IEC-60958 and IEC-61937. + +properties: + compatible: + enum: + - st,stm32h7-spdifrx + + "#sound-dai-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: kclk + + interrupts: + maxItems: 1 + + dmas: + items: + - description: audio data capture DMA + - description: IEC status bits capture DMA + + dma-names: + items: + - const: rx + - const: rx-ctrl + + resets: + maxItems: 1 + +required: + - compatible + - "#sound-dai-cells" + - reg + - clocks + - clock-names + - interrupts + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/stm32mp1-clks.h> + spdifrx: spdifrx@40004000 { + compatible = "st,stm32h7-spdifrx"; + #sound-dai-cells = <0>; + reg = <0x40004000 0x400>; + clocks = <&rcc SPDIF_K>; + clock-names = "kclk"; + interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmamux1 2 93 0x400 0x0>, + <&dmamux1 3 94 0x400 0x0>; + dma-names = "rx", "rx-ctrl"; + pinctrl-0 = <&spdifrx_pins>; + pinctrl-names = "default"; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/tas2562.txt b/Documentation/devicetree/bindings/sound/tas2562.txt index 658e1fb18a99..94796b547184 100644 --- a/Documentation/devicetree/bindings/sound/tas2562.txt +++ b/Documentation/devicetree/bindings/sound/tas2562.txt @@ -8,7 +8,7 @@ real time monitoring of loudspeaker behavior. Required properties: - #address-cells - Should be <1>. - #size-cells - Should be <0>. - - compatible: - Should contain "ti,tas2562". + - compatible: - Should contain "ti,tas2562", "ti,tas2563". - reg: - The i2c address. Should be 0x4c, 0x4d, 0x4e or 0x4f. - ti,imon-slot-no:- TDM TX current sense time slot. diff --git a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml new file mode 100644 index 000000000000..ab2268c0ee67 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause) +# Copyright (C) 2019 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/tlv320adcx140.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TLV320ADCX140 Quad Channel Analog-to-Digital Converter + +maintainers: + - Dan Murphy <dmurphy@ti.com> + +description: | + The TLV320ADCX140 are multichannel (4-ch analog recording or 8-ch digital + PDM microphones recording), high-performance audio, analog-to-digital + converter (ADC) with analog inputs supporting up to 2V RMS. The TLV320ADCX140 + family supports line and microphone Inputs, and offers a programmable + microphone bias or supply voltage generation. + + Specifications can be found at: + http://www.ti.com/lit/ds/symlink/tlv320adc3140.pdf + http://www.ti.com/lit/ds/symlink/tlv320adc5140.pdf + http://www.ti.com/lit/ds/symlink/tlv320adc6140.pdf + +properties: + compatible: + oneOf: + - const: ti,tlv320adc3140 + - const: ti,tlv320adc5140 + - const: ti,tlv320adc6140 + + reg: + maxItems: 1 + description: | + I2C addresss of the device can be one of these 0x4c, 0x4d, 0x4e or 0x4f + + reset-gpios: + description: | + GPIO used for hardware reset. + + areg-supply: + description: | + Regulator with AVDD at 3.3V. If not defined then the internal regulator + is enabled. + + ti,mic-bias-source: + description: | + Indicates the source for MIC Bias. + 0 - Mic bias is set to VREF + 1 - Mic bias is set to VREF × 1.096 + 6 - Mic bias is set to AVDD + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [0, 1, 6] + + ti,vref-source: + description: | + Indicates the source for MIC Bias. + 0 - Set VREF to 2.75V + 1 - Set VREF to 2.5V + 2 - Set VREF to 1.375V + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [0, 1, 2] + +required: + - compatible + - reg + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + codec: codec@4c { + compatible = "ti,tlv320adc5140"; + reg = <0x4c>; + ti,mic-bias-source = <6>; + reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index 392875a1b94e..72f97d4b01a7 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -2234,6 +2234,19 @@ use_vmalloc buffers. If mmap is used on such architectures, turn off this option, so that the DMA-coherent buffers are allocated and used instead. +delayed_register + The option is needed for devices that have multiple streams + defined in multiple USB interfaces. The driver may invoke + registrations multiple times (once per interface) and this may + lead to the insufficient device enumeration. + This option receives an array of strings, and you can pass + ID:INTERFACE like ``0123abcd:4`` for performing the delayed + registration to the given device. In this example, when a USB + device 0123:abcd is probed, the driver waits the registration + until the USB interface 4 gets probed. + The driver prints a message like "Found post-registration device + assignment: 1234abcd:04" for such a device, so that user can + notice the need. This module supports multiple devices, autoprobe and hotplugging. diff --git a/Documentation/sound/hd-audio/index.rst b/Documentation/sound/hd-audio/index.rst index f8a72ffffe66..6e12de9fc34e 100644 --- a/Documentation/sound/hd-audio/index.rst +++ b/Documentation/sound/hd-audio/index.rst @@ -8,3 +8,4 @@ HD-Audio models controls dp-mst + realtek-pc-beep diff --git a/Documentation/sound/hd-audio/models.rst b/Documentation/sound/hd-audio/models.rst index 11298f0ce44d..0ea967d34583 100644 --- a/Documentation/sound/hd-audio/models.rst +++ b/Documentation/sound/hd-audio/models.rst @@ -216,8 +216,6 @@ alc298-dell-aio ALC298 fixups on Dell AIO machines alc275-dell-xps ALC275 fixups on Dell XPS models -alc256-dell-xps13 - ALC256 fixups on Dell XPS13 lenovo-spk-noise Workaround for speaker noise on Lenovo machines lenovo-hotkey diff --git a/Documentation/sound/hd-audio/realtek-pc-beep.rst b/Documentation/sound/hd-audio/realtek-pc-beep.rst new file mode 100644 index 000000000000..be47c6f76a6e --- /dev/null +++ b/Documentation/sound/hd-audio/realtek-pc-beep.rst @@ -0,0 +1,129 @@ +=============================== +Realtek PC Beep Hidden Register +=============================== + +This file documents the "PC Beep Hidden Register", which is present in certain +Realtek HDA codecs and controls a muxer and pair of passthrough mixers that can +route audio between pins but aren't themselves exposed as HDA widgets. As far +as I can tell, these hidden routes are designed to allow flexible PC Beep output +for codecs that don't have mixer widgets in their output paths. Why it's easier +to hide a mixer behind an undocumented vendor register than to just expose it +as a widget, I have no idea. + +Register Description +==================== + +The register is accessed via processing coefficient 0x36 on NID 20h. Bits not +identified below have no discernible effect on my machine, a Dell XPS 13 9350:: + + MSB LSB + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | |h|S|L| | B |R| | Known bits + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + |0|0|1|1| 0x7 |0|0x0|1| 0x7 | Reset value + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +1Ah input select (B): 2 bits + When zero, expose the PC Beep line (from the internal beep generator, when + enabled with the Set Beep Generation verb on NID 01h, or else from the + external PCBEEP pin) on the 1Ah pin node. When nonzero, expose the headphone + jack (or possibly Line In on some machines) input instead. If PC Beep is + selected, the 1Ah boost control has no effect. + +Amplify 1Ah loopback, left (L): 1 bit + Amplify the left channel of 1Ah before mixing it into outputs as specified + by h and S bits. Does not affect the level of 1Ah exposed to other widgets. + +Amplify 1Ah loopback, right (R): 1 bit + Amplify the right channel of 1Ah before mixing it into outputs as specified + by h and S bits. Does not affect the level of 1Ah exposed to other widgets. + +Loopback 1Ah to 21h [active low] (h): 1 bit + When zero, mix 1Ah (possibly with amplification, depending on L and R bits) + into 21h (headphone jack on my machine). Mixed signal respects the mute + setting on 21h. + +Loopback 1Ah to 14h (S): 1 bit + When one, mix 1Ah (possibly with amplification, depending on L and R bits) + into 14h (internal speaker on my machine). Mixed signal **ignores** the mute + setting on 14h and is present whenever 14h is configured as an output. + +Path diagrams +============= + +1Ah input selection (DIV is the PC Beep divider set on NID 01h):: + + <Beep generator> <PCBEEP pin> <Headphone jack> + | | | + +--DIV--+--!DIV--+ {1Ah boost control} + | | + +--(b == 0)--+--(b != 0)--+ + | + >1Ah (Beep/Headphone Mic/Line In)< + +Loopback of 1Ah to 21h/14h:: + + <1Ah (Beep/Headphone Mic/Line In)> + | + {amplify if L/R} + | + +-----!h-----+-----S-----+ + | | + {21h mute control} | + | | + >21h (Headphone)< >14h (Internal Speaker)< + +Background +========== + +All Realtek HDA codecs have a vendor-defined widget with node ID 20h which +provides access to a bank of registers that control various codec functions. +Registers are read and written via the standard HDA processing coefficient +verbs (Set/Get Coefficient Index, Set/Get Processing Coefficient). The node is +named "Realtek Vendor Registers" in public datasheets' verb listings and, +apart from that, is entirely undocumented. + +This particular register, exposed at coefficient 0x36 and named in commits from +Realtek, is of note: unlike most registers, which seem to control detailed +amplifier parameters not in scope of the HDA specification, it controls audio +routing which could just as easily have been defined using standard HDA mixer +and selector widgets. + +Specifically, it selects between two sources for the input pin widget with Node +ID (NID) 1Ah: the widget's signal can come either from an audio jack (on my +laptop, a Dell XPS 13 9350, it's the headphone jack, but comments in Realtek +commits indicate that it might be a Line In on some machines) or from the PC +Beep line (which is itself multiplexed between the codec's internal beep +generator and external PCBEEP pin, depending on if the beep generator is +enabled via verbs on NID 01h). Additionally, it can mix (with optional +amplification) that signal onto the 21h and/or 14h output pins. + +The register's reset value is 0x3717, corresponding to PC Beep on 1Ah that is +then amplified and mixed into both the headphones and the speakers. Not only +does this violate the HDA specification, which says that "[a vendor defined +beep input pin] connection may be maintained *only* while the Link reset +(**RST#**) is asserted", it means that we cannot ignore the register if we care +about the input that 1Ah would otherwise expose or if the PCBEEP trace is +poorly shielded and picks up chassis noise (both of which are the case on my +machine). + +Unfortunately, there are lots of ways to get this register configuration wrong. +Linux, it seems, has gone through most of them. For one, the register resets +after S3 suspend: judging by existing code, this isn't the case for all vendor +registers, and it's led to some fixes that improve behavior on cold boot but +don't last after suspend. Other fixes have successfully switched the 1Ah input +away from PC Beep but have failed to disable both loopback paths. On my +machine, this means that the headphone input is amplified and looped back to +the headphone output, which uses the exact same pins! As you might expect, this +causes terrible headphone noise, the character of which is controlled by the +1Ah boost control. (If you've seen instructions online to fix XPS 13 headphone +noise by changing "Headphone Mic Boost" in ALSA, now you know why.) + +The information here has been obtained through black-box reverse engineering of +the ALC256 codec's behavior and is not guaranteed to be correct. It likely +also applies for the ALC255, ALC257, ALC235, and ALC236, since those codecs +seem to be close relatives of the ALC256. (They all share one initialization +function.) Additionally, other codecs like the ALC225 and ALC285 also have this +register, judging by existing fixups in ``patch_realtek.c``, but specific +data (e.g. node IDs, bit positions, pin mappings) for those codecs may differ +from what I've described here. diff --git a/Documentation/sound/soc/codec-to-codec.rst b/Documentation/sound/soc/codec-to-codec.rst index 810109d7500d..4eaa9a0c41fc 100644 --- a/Documentation/sound/soc/codec-to-codec.rst +++ b/Documentation/sound/soc/codec-to-codec.rst @@ -104,5 +104,10 @@ Make sure to name your corresponding cpu and codec playback and capture dai names ending with "Playback" and "Capture" respectively as dapm core will link and power those dais based on the name. -Note that in current device tree there is no way to mark a dai_link -as codec to codec. However, it may change in future. +A dai_link in a "simple-audio-card" will automatically be detected as +codec to codec when all DAIs on the link belong to codec components. +The dai_link will be initialized with the subset of stream parameters +(channels, format, sample rate) supported by all DAIs on the link. Since +there is no way to provide these parameters in the device tree, this is +mostly useful for communication with simple fixed-function codecs, such +as a Bluetooth controller or cellular modem. diff --git a/MAINTAINERS b/MAINTAINERS index 0ffb16414b9b..d3d6ea91fe28 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4055,8 +4055,8 @@ F: Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml F: sound/soc/codecs/cros_ec_codec.* CIRRUS LOGIC AUDIO CODEC DRIVERS -M: Brian Austin <brian.austin@cirrus.com> -M: Paul Handrigan <Paul.Handrigan@cirrus.com> +M: James Schulman <james.schulman@cirrus.com> +M: David Rhodes <david.rhodes@cirrus.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: sound/soc/codecs/cs* @@ -15750,6 +15750,17 @@ F: sound/soc/ F: include/dt-bindings/sound/ F: include/sound/soc* +SOUND - SOUND OPEN FIRMWARE (SOF) DRIVERS +M: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> +M: Liam Girdwood <lgirdwood@gmail.com> +M: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> +M: Kai Vehmanen <kai.vehmanen@linux.intel.com> +M: Daniel Baluta <daniel.baluta@nxp.com> +L: sound-open-firmware@alsa-project.org (moderated for non-subscribers) +W: https://github.com/thesofproject/linux/ +S: Supported +F: sound/soc/sof/ + SOUNDWIRE SUBSYSTEM M: Vinod Koul <vkoul@kernel.org> M: Sanyog Kale <sanyog.r.kale@intel.com> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index a8b20557539b..ff43a3d80410 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -12,6 +12,7 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/mfd/syscon.h> +#include <linux/mutex.h> #include <linux/of_platform.h> #include <linux/of.h> #include <linux/of_gpio.h> @@ -169,6 +170,9 @@ struct mtk_hdmi { bool audio_enable; bool powered; bool enabled; + hdmi_codec_plugged_cb plugged_cb; + struct device *codec_dev; + struct mutex update_plugged_status_lock; }; static inline struct mtk_hdmi *hdmi_ctx_from_bridge(struct drm_bridge *b) @@ -1194,13 +1198,26 @@ static void mtk_hdmi_clk_disable_audio(struct mtk_hdmi *hdmi) clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]); } +static enum drm_connector_status +mtk_hdmi_update_plugged_status(struct mtk_hdmi *hdmi) +{ + bool connected; + + mutex_lock(&hdmi->update_plugged_status_lock); + connected = mtk_cec_hpd_high(hdmi->cec_dev); + if (hdmi->plugged_cb && hdmi->codec_dev) + hdmi->plugged_cb(hdmi->codec_dev, connected); + mutex_unlock(&hdmi->update_plugged_status_lock); + + return connected ? + connector_status_connected : connector_status_disconnected; +} + static enum drm_connector_status hdmi_conn_detect(struct drm_connector *conn, bool force) { struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); - - return mtk_cec_hpd_high(hdmi->cec_dev) ? - connector_status_connected : connector_status_disconnected; + return mtk_hdmi_update_plugged_status(hdmi); } static void hdmi_conn_destroy(struct drm_connector *conn) @@ -1657,20 +1674,39 @@ static int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, return 0; } +static int mtk_hdmi_audio_hook_plugged_cb(struct device *dev, void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct mtk_hdmi *hdmi = data; + + mutex_lock(&hdmi->update_plugged_status_lock); + hdmi->plugged_cb = fn; + hdmi->codec_dev = codec_dev; + mutex_unlock(&hdmi->update_plugged_status_lock); + + mtk_hdmi_update_plugged_status(hdmi); + + return 0; +} + static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = { .hw_params = mtk_hdmi_audio_hw_params, .audio_startup = mtk_hdmi_audio_startup, .audio_shutdown = mtk_hdmi_audio_shutdown, .digital_mute = mtk_hdmi_audio_digital_mute, .get_eld = mtk_hdmi_audio_get_eld, + .hook_plugged_cb = mtk_hdmi_audio_hook_plugged_cb, }; -static void mtk_hdmi_register_audio_driver(struct device *dev) +static int mtk_hdmi_register_audio_driver(struct device *dev) { + struct mtk_hdmi *hdmi = dev_get_drvdata(dev); struct hdmi_codec_pdata codec_data = { .ops = &mtk_hdmi_audio_codec_ops, .max_i2s_channels = 2, .i2s = 1, + .data = hdmi, }; struct platform_device *pdev; @@ -1678,9 +1714,10 @@ static void mtk_hdmi_register_audio_driver(struct device *dev) PLATFORM_DEVID_AUTO, &codec_data, sizeof(codec_data)); if (IS_ERR(pdev)) - return; + return PTR_ERR(pdev); DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME); + return 0; } static int mtk_drm_hdmi_probe(struct platform_device *pdev) @@ -1706,6 +1743,7 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev) return ret; } + mutex_init(&hdmi->update_plugged_status_lock); platform_set_drvdata(pdev, hdmi); ret = mtk_hdmi_output_init(hdmi); @@ -1714,7 +1752,11 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev) return ret; } - mtk_hdmi_register_audio_driver(dev); + ret = mtk_hdmi_register_audio_driver(dev); + if (ret) { + dev_err(dev, "Failed to register audio driver: %d\n", ret); + return ret; + } hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs; hdmi->bridge.of_node = pdev->dev.of_node; diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 1c6c6a2e0def..440effed6df6 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -594,6 +594,7 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev); struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sdw_stream_runtime *sruntime; + struct snd_soc_dai *codec_dai; int ret, i; sruntime = sdw_alloc_stream(dai->name); @@ -602,12 +603,12 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, ctrl->sruntime[dai->id] = sruntime; - for (i = 0; i < rtd->num_codecs; i++) { - ret = snd_soc_dai_set_sdw_stream(rtd->codec_dais[i], sruntime, + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_sdw_stream(codec_dai, sruntime, substream->stream); if (ret < 0 && ret != -ENOTSUPP) { dev_err(dai->dev, "Failed to set sdw stream on %s", - rtd->codec_dais[i]->name); + codec_dai->name); sdw_release_stream(sruntime); return ret; } diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 178ae92b8cc1..7fb89a94d9c0 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -167,13 +167,15 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus, return ret; } - /* Program DPN_BlockCtrl1 register */ - ret = sdw_write(s_rt->slave, addr2, (p_params->bps - 1)); - if (ret < 0) { - dev_err(&s_rt->slave->dev, - "DPN_BlockCtrl1 register write failed for port %d\n", - t_params->port_num); - return ret; + if (!dpn_prop->read_only_wordlength) { + /* Program DPN_BlockCtrl1 register */ + ret = sdw_write(s_rt->slave, addr2, (p_params->bps - 1)); + if (ret < 0) { + dev_err(&s_rt->slave->dev, + "DPN_BlockCtrl1 register write failed for port %d\n", + t_params->port_num); + return ret; + } } /* Program DPN_SampleCtrl1 register */ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index efce98e9844e..741b9140992a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -575,7 +575,7 @@ config SPI_PPC4xx config SPI_PXA2XX tristate "PXA2xx SSP SPI master" - depends on (ARCH_PXA || ARCH_MMP || PCI || ACPI) + depends on ARCH_PXA || ARCH_MMP || PCI || ACPI || COMPILE_TEST select PXA_SSP if ARCH_PXA || ARCH_MMP help This enables using a PXA2xx or Sodaville SSP port as a SPI master diff --git a/include/dt-bindings/sound/meson-aiu.h b/include/dt-bindings/sound/meson-aiu.h new file mode 100644 index 000000000000..1051b8af298b --- /dev/null +++ b/include/dt-bindings/sound/meson-aiu.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_MESON_AIU_H +#define __DT_MESON_AIU_H + +#define AIU_CPU 0 +#define AIU_HDMI 1 +#define AIU_ACODEC 2 + +#define CPU_I2S_FIFO 0 +#define CPU_SPDIF_FIFO 1 +#define CPU_I2S_ENCODER 2 +#define CPU_SPDIF_ENCODER 3 + +#define CTRL_I2S 0 +#define CTRL_PCM 1 +#define CTRL_OUT 2 + +#endif /* __DT_MESON_AIU_H */ diff --git a/include/dt-bindings/sound/meson-g12a-toacodec.h b/include/dt-bindings/sound/meson-g12a-toacodec.h new file mode 100644 index 000000000000..69d7a75592a2 --- /dev/null +++ b/include/dt-bindings/sound/meson-g12a-toacodec.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_MESON_G12A_TOACODEC_H +#define __DT_MESON_G12A_TOACODEC_H + +#define TOACODEC_IN_A 0 +#define TOACODEC_IN_B 1 +#define TOACODEC_IN_C 2 +#define TOACODEC_OUT 3 + +#endif /* __DT_MESON_G12A_TOACODEC_H */ diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index b451bb622335..2dfe14ed3bb0 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -284,6 +284,7 @@ struct sdw_dpn_audio_mode { * @max_async_buffer: Number of samples that this port can buffer in * asynchronous modes * @block_pack_mode: Type of block port mode supported + * @read_only_wordlength: Read Only wordlength field in DPN_BlockCtrl1 register * @port_encoding: Payload Channel Sample encoding schemes supported * @audio_modes: Audio modes supported */ @@ -307,6 +308,7 @@ struct sdw_dpn_prop { u32 modes; u32 max_async_buffer; bool block_pack_mode; + bool read_only_wordlength; u32 port_encoding; struct sdw_dpn_audio_mode *audio_modes; }; diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index 5e31740c7e40..ead8c9a47c6a 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -156,6 +156,18 @@ struct uac2_feature_unit_descriptor { __u8 bmaControls[]; /* variable length */ } __attribute__((packed)); +/* 4.7.2.10 Effect Unit Descriptor */ + +struct uac2_effect_unit_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bUnitID; + __le16 wEffectType; + __u8 bSourceID; + __u8 bmaControls[]; /* variable length */ +} __attribute__((packed)); + /* 4.9.2 Class-Specific AS Interface Descriptor */ struct uac2_as_header_descriptor { diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index bc88d6f964da..6ce8effa0b12 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -23,7 +23,6 @@ struct snd_compr_ops; * struct snd_compr_runtime: runtime stream description * @state: stream state * @ops: pointer to DSP callbacks - * @dma_buffer_p: runtime dma buffer pointer * @buffer: pointer to kernel buffer, valid only when not in mmap mode or * DSP doesn't implement copy * @buffer_size: size of the above buffer @@ -34,11 +33,14 @@ struct snd_compr_ops; * @total_bytes_transferred: cumulative bytes transferred by offload DSP * @sleep: poll sleep * @private_data: driver private data pointer + * @dma_area: virtual buffer address + * @dma_addr: physical buffer address (not accessible from main CPU) + * @dma_bytes: size of DMA area + * @dma_buffer_p: runtime dma buffer pointer */ struct snd_compr_runtime { snd_pcm_state_t state; struct snd_compr_ops *ops; - struct snd_dma_buffer *dma_buffer_p; void *buffer; u64 buffer_size; u32 fragment_size; @@ -47,6 +49,11 @@ struct snd_compr_runtime { u64 total_bytes_transferred; wait_queue_head_t sleep; void *private_data; + + unsigned char *dma_area; + dma_addr_t dma_addr; + size_t dma_bytes; + struct snd_dma_buffer *dma_buffer_p; }; /** @@ -60,6 +67,7 @@ struct snd_compr_runtime { * @metadata_set: metadata set flag, true when set * @next_track: has userspace signal next track transition, true when set * @private_data: pointer to DSP private data + * @dma_buffer: allocated buffer if any */ struct snd_compr_stream { const char *name; @@ -71,6 +79,7 @@ struct snd_compr_stream { bool metadata_set; bool next_track; void *private_data; + struct snd_dma_buffer dma_buffer; }; /** @@ -180,21 +189,34 @@ static inline void snd_compr_drain_notify(struct snd_compr_stream *stream) /** * snd_compr_set_runtime_buffer - Set the Compress runtime buffer - * @substream: compress substream to set + * @stream: compress stream to set * @bufp: the buffer information, NULL to clear * * Copy the buffer information to runtime buffer when @bufp is non-NULL. * Otherwise it clears the current buffer information. */ -static inline void snd_compr_set_runtime_buffer( - struct snd_compr_stream *substream, - struct snd_dma_buffer *bufp) +static inline void +snd_compr_set_runtime_buffer(struct snd_compr_stream *stream, + struct snd_dma_buffer *bufp) { - struct snd_compr_runtime *runtime = substream->runtime; - - runtime->dma_buffer_p = bufp; + struct snd_compr_runtime *runtime = stream->runtime; + + if (bufp) { + runtime->dma_buffer_p = bufp; + runtime->dma_area = bufp->area; + runtime->dma_addr = bufp->addr; + runtime->dma_bytes = bufp->bytes; + } else { + runtime->dma_buffer_p = NULL; + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } } +int snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size); +int snd_compr_free_pages(struct snd_compr_stream *stream); + int snd_compr_stop_error(struct snd_compr_stream *stream, snd_pcm_state_t state); diff --git a/include/sound/core.h b/include/sound/core.h index ac8b692b69b4..381a010a1bd4 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -266,6 +266,7 @@ void snd_device_disconnect(struct snd_card *card, void *device_data); void snd_device_disconnect_all(struct snd_card *card); void snd_device_free(struct snd_card *card, void *device_data); void snd_device_free_all(struct snd_card *card); +int snd_device_get_state(struct snd_card *card, void *device_data); /* isadma.c */ diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index d4299e146d95..affedc2801c4 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -513,6 +513,7 @@ struct hdac_stream { struct snd_pcm_substream *substream; /* assigned substream, * set in PCM open */ + struct snd_compr_stream *cstream; unsigned int format_val; /* format value to be set in the * controller and the codec */ @@ -527,6 +528,7 @@ struct hdac_stream { bool locked:1; bool stripe:1; /* apply stripe control */ + u64 curr_pos; /* timestamp */ unsigned long start_wallclk; /* start + minimum wallclk */ unsigned long period_wallclk; /* wallclk for period */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index f657ff08f317..2ba5df2c9e23 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -644,6 +644,11 @@ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, #define snd_pcm_group_for_each_entry(s, substream) \ list_for_each_entry(s, &substream->group->substreams, link_list) +#define for_each_pcm_streams(stream) \ + for (stream = SNDRV_PCM_STREAM_PLAYBACK; \ + stream <= SNDRV_PCM_STREAM_LAST; \ + stream++) + /** * snd_pcm_running - Check whether the substream is in a running state * @substream: substream to check @@ -1122,7 +1127,14 @@ snd_pcm_kernel_readv(struct snd_pcm_substream *substream, return __snd_pcm_lib_xfer(substream, bufs, false, frames, true); } -int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime); +int snd_pcm_hw_limit_rates(struct snd_pcm_hardware *hw); + +static inline int +snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) +{ + return snd_pcm_hw_limit_rates(&runtime->hw); +} + unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit); unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, @@ -1415,6 +1427,15 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) return 1ULL << (__force int) pcm_format; } +/** + * pcm_for_each_format - helper to iterate for each format type + * @f: the iterator variable in snd_pcm_format_t type + */ +#define pcm_for_each_format(f) \ + for ((f) = SNDRV_PCM_FORMAT_FIRST; \ + (__force int)(f) <= (__force int)SNDRV_PCM_FORMAT_LAST; \ + (f) = (__force snd_pcm_format_t)((__force int)(f) + 1)) + /* printk helpers */ #define pcm_err(pcm, fmt, args...) \ dev_err((pcm)->card->dev, fmt, ##args) diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index 661450a2095b..36f94735d23d 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -133,6 +133,13 @@ static inline int snd_mask_test(const struct snd_mask *mask, unsigned int val) return mask->bits[MASK_OFS(val)] & MASK_BIT(val); } +/* Most of drivers need only this one */ +static inline int snd_mask_test_format(const struct snd_mask *mask, + snd_pcm_format_t format) +{ + return snd_mask_test(mask, (__force unsigned int)format); +} + static inline int snd_mask_single(const struct snd_mask *mask) { int i, c = 0; diff --git a/include/sound/rt5682.h b/include/sound/rt5682.h index bc2c31734df1..e1f790561ac1 100644 --- a/include/sound/rt5682.h +++ b/include/sound/rt5682.h @@ -24,6 +24,12 @@ enum rt5682_jd_src { RT5682_JD1, }; +enum rt5682_dai_clks { + RT5682_DAI_WCLK_IDX, + RT5682_DAI_BCLK_IDX, + RT5682_DAI_NUM_CLKS, +}; + struct rt5682_platform_data { int ldo1_en; /* GPIO for LDO1_EN */ @@ -32,6 +38,10 @@ struct rt5682_platform_data { enum rt5682_dmic1_clk_pin dmic1_clk_pin; enum rt5682_jd_src jd_src; unsigned int btndet_delay; + unsigned int dmic_clk_rate; + unsigned int dmic_delay; + + const char *dai_clk_names[RT5682_DAI_NUM_CLKS]; }; #endif diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index a217a87cae86..392e953d561e 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -75,18 +75,45 @@ struct snd_soc_acpi_mach_params { }; /** - * snd_soc_acpi_link_adr: ACPI-based list of _ADR, with a variable - * number of devices per link - * + * snd_soc_acpi_endpoint - endpoint descriptor + * @num: endpoint number (mandatory, unique per device) + * @aggregated: 0 (independent) or 1 (logically grouped) + * @group_position: zero-based order (only when @aggregated is 1) + * @group_id: platform-unique group identifier (only when @aggregrated is 1) + */ +struct snd_soc_acpi_endpoint { + u8 num; + u8 aggregated; + u8 group_position; + u8 group_id; +}; + +/** + * snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device + * @adr: 64 bit ACPI _ADR value + * @num_endpoints: number of endpoints for this device + * @endpoints: array of endpoints + */ +struct snd_soc_acpi_adr_device { + const u64 adr; + const u8 num_endpoints; + const struct snd_soc_acpi_endpoint *endpoints; +}; + +/** + * snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices * @mask: one bit set indicates the link this list applies to - * @num_adr: ARRAY_SIZE of adr - * @adr: array of _ADR (represented as u64). + * @num_adr: ARRAY_SIZE of devices + * @adr_d: array of devices + * + * The number of devices per link can be more than 1, e.g. in SoundWire + * multi-drop configurations. */ struct snd_soc_acpi_link_adr { const u32 mask; const u32 num_adr; - const u64 *adr; + const struct snd_soc_acpi_adr_device *adr_d; }; /** diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index eaaeb00e9e84..78bac995db15 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -202,6 +202,8 @@ struct snd_soc_dai_ops { int (*set_sdw_stream)(struct snd_soc_dai *dai, void *stream, int direction); + void *(*get_sdw_stream)(struct snd_soc_dai *dai, int direction); + /* * DAI digital mute - optional. * Called by soc-core to minimise any pops. @@ -322,9 +324,7 @@ struct snd_soc_dai { struct snd_soc_dai_driver *driver; /* DAI runtime info */ - unsigned int capture_active; /* stream usage count */ - unsigned int playback_active; /* stream usage count */ - unsigned int probed:1; + unsigned int stream_active[SNDRV_PCM_STREAM_LAST + 1]; /* usage count */ unsigned int active; @@ -348,8 +348,27 @@ struct snd_soc_dai { unsigned int rx_mask; struct list_head list; + + /* bit field */ + unsigned int probed:1; + unsigned int started:1; }; +static inline struct snd_soc_pcm_stream * +snd_soc_dai_get_pcm_stream(const struct snd_soc_dai *dai, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + &dai->driver->playback : &dai->driver->capture; +} + +static inline +struct snd_soc_dapm_widget *snd_soc_dai_get_widget( + struct snd_soc_dai *dai, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + dai->playback_widget : dai->capture_widget; +} + static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai, const struct snd_pcm_substream *ss) { @@ -406,4 +425,23 @@ static inline int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai, return -ENOTSUPP; } +/** + * snd_soc_dai_get_sdw_stream() - Retrieves SDW 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() + * + * Returns pointer to stream or -ENOTSUPP if callback is not supported; + */ +static inline void *snd_soc_dai_get_sdw_stream(struct snd_soc_dai *dai, + int direction) +{ + if (dai->driver->ops->get_sdw_stream) + return dai->driver->ops->get_sdw_stream(dai, direction); + else + return ERR_PTR(-ENOTSUPP); +} + #endif diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 1b6afbc1a4ed..08495f8d86dc 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -482,6 +482,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list, bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, enum snd_soc_dapm_direction)); +void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list); struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( struct snd_kcontrol *kcontrol); @@ -691,6 +692,11 @@ struct snd_soc_dapm_widget_list { struct snd_soc_dapm_widget *widgets[0]; }; +#define for_each_dapm_widgets(list, i, widget) \ + for ((i) = 0; \ + (i) < list->num_widgets && (widget = list->widgets[i]); \ + (i)++) + struct snd_soc_dapm_stats { int power_checks; int path_checks; diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index b654ebfc8766..0f6c50b17bba 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -132,17 +132,8 @@ int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, struct snd_pcm_substream * snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream); -/* get the BE runtime state */ -enum snd_soc_dpcm_state - snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream); - -/* set the BE runtime state */ -void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream, - enum snd_soc_dpcm_state state); - -/* internal use only */ -int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); -int soc_dpcm_runtime_update(struct snd_soc_card *); +/* update audio routing between PCMs and any DAI links */ +int snd_soc_dpcm_runtime_update(struct snd_soc_card *card); #ifdef CONFIG_DEBUG_FS void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); @@ -154,6 +145,7 @@ static inline void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) int dpcm_path_get(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_); +void dpcm_path_put(struct snd_soc_dapm_widget_list **list); int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list, int new); int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream); @@ -167,10 +159,4 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event); -static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) -{ - kfree(*list); -} - - #endif diff --git a/include/sound/soc.h b/include/sound/soc.h index 8a2266676b2d..13458e4fbb13 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -471,6 +471,9 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd); void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream); void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream); +int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hardware *hw, int stream); + int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, unsigned int dai_fmt); @@ -855,6 +858,11 @@ struct snd_soc_dai_link { ((platform) = &link->platforms[i]); \ (i)++) +#define for_each_link_cpus(link, i, cpu) \ + for ((i) = 0; \ + ((i) < link->num_cpus) && ((cpu) = &link->cpus[i]); \ + (i)++) + /* * Sample 1 : Single CPU/Codec/Platform * @@ -1058,6 +1066,7 @@ struct snd_soc_card { const struct snd_soc_dapm_route *of_dapm_routes; int num_of_dapm_routes; bool fully_routed; + bool disable_route_checks; /* lists of probed devices belonging to this card */ struct list_head component_dev_list; @@ -1109,6 +1118,14 @@ struct snd_soc_card { #define for_each_card_components(card, component) \ list_for_each_entry(component, &(card)->component_dev_list, card_list) +#define for_each_card_dapms(card, dapm) \ + list_for_each_entry(dapm, &card->dapm_list, list) + +#define for_each_card_widgets(card, w)\ + list_for_each_entry(w, &card->widgets, list) +#define for_each_card_widgets_safe(card, w, _w) \ + list_for_each_entry_safe(w, _w, &card->widgets, list) + /* SoC machine DAI configuration, glues a codec and cpu DAI together */ struct snd_soc_pcm_runtime { struct device *dev; @@ -1128,10 +1145,14 @@ struct snd_soc_pcm_runtime { struct snd_compr *compr; struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; + struct snd_soc_dai **dais; struct snd_soc_dai **codec_dais; unsigned int num_codecs; + struct snd_soc_dai **cpu_dais; + unsigned int num_cpus; + struct delayed_work delayed_work; void (*close_delayed_work_func)(struct snd_soc_pcm_runtime *rtd); #ifdef CONFIG_DEBUG_FS @@ -1148,16 +1169,31 @@ struct snd_soc_pcm_runtime { int num_components; struct snd_soc_component *components[0]; /* CPU/Codec/Platform */ }; +/* see soc_new_pcm_runtime() */ +#define asoc_rtd_to_cpu(rtd, n) (rtd)->dais[n] +#define asoc_rtd_to_codec(rtd, n) (rtd)->dais[n + (rtd)->num_cpus] + #define for_each_rtd_components(rtd, i, component) \ for ((i) = 0; \ ((i) < rtd->num_components) && ((component) = rtd->components[i]);\ (i)++) -#define for_each_rtd_codec_dai(rtd, i, dai)\ - for ((i) = 0; \ - ((i) < rtd->num_codecs) && ((dai) = rtd->codec_dais[i]); \ +#define for_each_rtd_cpu_dais(rtd, i, dai) \ + for ((i) = 0; \ + ((i) < rtd->num_cpus) && ((dai) = rtd->cpu_dais[i]); \ (i)++) -#define for_each_rtd_codec_dai_rollback(rtd, i, dai) \ +#define for_each_rtd_cpu_dais_rollback(rtd, i, dai) \ + for (; (--(i) >= 0) && ((dai) = rtd->cpu_dais[i]);) +#define for_each_rtd_codec_dais(rtd, i, dai) \ + for ((i) = 0; \ + ((i) < rtd->num_codecs) && ((dai) = rtd->codec_dais[i]); \ + (i)++) +#define for_each_rtd_codec_dais_rollback(rtd, i, dai) \ for (; (--(i) >= 0) && ((dai) = rtd->codec_dais[i]);) +#define for_each_rtd_dais(rtd, i, dai) \ + for ((i) = 0; \ + ((i) < (rtd)->num_cpus + (rtd)->num_codecs) && \ + ((dai) = (rtd)->dais[i]); \ + (i)++) void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd); diff --git a/include/sound/sof/dai-intel.h b/include/sound/sof/dai-intel.h index 5f1ef5565be6..04e48227f542 100644 --- a/include/sound/sof/dai-intel.h +++ b/include/sound/sof/dai-intel.h @@ -87,6 +87,15 @@ struct sof_ipc_dai_hda_params { uint32_t link_dma_ch; } __packed; +/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */ +struct sof_ipc_dai_alh_params { + struct sof_ipc_hdr hdr; + uint32_t stream_id; + + /* reserved for future use */ + uint32_t reserved[15]; +} __packed; + /* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */ /* This struct is defined per 2ch PDM controller available in the platform. @@ -179,13 +188,4 @@ struct sof_ipc_dai_dmic_params { struct sof_ipc_dai_dmic_pdm_ctrl pdm[0]; } __packed; -/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */ -struct sof_ipc_dai_alh_params { - struct sof_ipc_hdr hdr; - uint32_t stream_id; - - /* reserved for future use */ - uint32_t reserved[15]; -} __packed; - #endif diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index bf3edd9c08b4..b79479575cc8 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -51,6 +51,7 @@ #define SOF_IPC_GLB_TRACE_MSG SOF_GLB_TYPE(0x9U) #define SOF_IPC_GLB_GDB_DEBUG SOF_GLB_TYPE(0xAU) #define SOF_IPC_GLB_TEST_MSG SOF_GLB_TYPE(0xBU) +#define SOF_IPC_GLB_PROBE SOF_GLB_TYPE(0xCU) /* * DSP Command Message Types @@ -102,6 +103,16 @@ #define SOF_IPC_STREAM_VORBIS_PARAMS SOF_CMD_TYPE(0x010) #define SOF_IPC_STREAM_VORBIS_FREE SOF_CMD_TYPE(0x011) +/* probe */ +#define SOF_IPC_PROBE_INIT SOF_CMD_TYPE(0x001) +#define SOF_IPC_PROBE_DEINIT SOF_CMD_TYPE(0x002) +#define SOF_IPC_PROBE_DMA_ADD SOF_CMD_TYPE(0x003) +#define SOF_IPC_PROBE_DMA_INFO SOF_CMD_TYPE(0x004) +#define SOF_IPC_PROBE_DMA_REMOVE SOF_CMD_TYPE(0x005) +#define SOF_IPC_PROBE_POINT_ADD SOF_CMD_TYPE(0x006) +#define SOF_IPC_PROBE_POINT_INFO SOF_CMD_TYPE(0x007) +#define SOF_IPC_PROBE_POINT_REMOVE SOF_CMD_TYPE(0x008) + /* trace */ #define SOF_IPC_TRACE_DMA_PARAMS SOF_CMD_TYPE(0x001) #define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002) diff --git a/include/sound/sof/info.h b/include/sound/sof/info.h index 1c560144996c..438a11fcf272 100644 --- a/include/sound/sof/info.h +++ b/include/sound/sof/info.h @@ -28,9 +28,9 @@ /* extended data types that can be appended onto end of sof_ipc_fw_ready */ enum sof_ipc_ext_data { - SOF_IPC_EXT_DMA_BUFFER = 0, - SOF_IPC_EXT_WINDOW, - SOF_IPC_EXT_CC_INFO, + SOF_IPC_EXT_UNUSED = 0, + SOF_IPC_EXT_WINDOW = 1, + SOF_IPC_EXT_CC_INFO = 2, }; /* FW version - SOF_IPC_GLB_VERSION */ @@ -83,22 +83,6 @@ struct sof_ipc_ext_data_hdr { uint32_t type; /**< SOF_IPC_EXT_ */ } __packed; -struct sof_ipc_dma_buffer_elem { - struct sof_ipc_hdr hdr; - uint32_t type; /**< SOF_IPC_REGION_ */ - uint32_t id; /**< platform specific - used to map to host memory */ - struct sof_ipc_host_buffer buffer; -} __packed; - -/* extended data DMA buffers for IPC, trace and debug */ -struct sof_ipc_dma_buffer_data { - struct sof_ipc_ext_data_hdr ext_hdr; - uint32_t num_buffers; - - /* host files in buffer[n].buffer */ - struct sof_ipc_dma_buffer_elem buffer[]; -} __packed; - struct sof_ipc_window_elem { struct sof_ipc_hdr hdr; uint32_t type; /**< SOF_IPC_REGION_ */ diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h index 8e76178fedf0..402e0250c508 100644 --- a/include/sound/sof/topology.h +++ b/include/sound/sof/topology.h @@ -53,9 +53,10 @@ struct sof_ipc_comp { uint32_t id; enum sof_comp_type type; uint32_t pipeline_id; + uint32_t core; /* reserved for future use */ - uint32_t reserved[2]; + uint32_t reserved[1]; } __packed; /* diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 6048553c119d..a74ca232f1fc 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -17,6 +17,7 @@ #define __LINUX_UAPI_SND_ASOC_H #include <linux/types.h> +#include <sound/asound.h> /* * Maximum number of channels topology kcontrol can represent. diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 56d95673ce0f..7184265c0b0d 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -31,7 +31,7 @@ #include <sound/compress_params.h> -#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 2) +#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 2, 0) /** * struct snd_compressed_buffer - compressed buffer * @fragment_size: size of buffer fragment in bytes diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h index 9c96fb0e4d90..79b14389ae41 100644 --- a/include/uapi/sound/compress_params.h +++ b/include/uapi/sound/compress_params.h @@ -75,7 +75,9 @@ #define SND_AUDIOCODEC_G723_1 ((__u32) 0x0000000C) #define SND_AUDIOCODEC_G729 ((__u32) 0x0000000D) #define SND_AUDIOCODEC_BESPOKE ((__u32) 0x0000000E) -#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_BESPOKE +#define SND_AUDIOCODEC_ALAC ((__u32) 0x0000000F) +#define SND_AUDIOCODEC_APE ((__u32) 0x00000010) +#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_APE /* * Profile and modes are listed with bit masks. This allows for a @@ -142,6 +144,9 @@ #define SND_AUDIOPROFILE_WMA8 ((__u32) 0x00000002) #define SND_AUDIOPROFILE_WMA9 ((__u32) 0x00000004) #define SND_AUDIOPROFILE_WMA10 ((__u32) 0x00000008) +#define SND_AUDIOPROFILE_WMA9_PRO ((__u32) 0x00000010) +#define SND_AUDIOPROFILE_WMA9_LOSSLESS ((__u32) 0x00000020) +#define SND_AUDIOPROFILE_WMA10_LOSSLESS ((__u32) 0x00000040) #define SND_AUDIOMODE_WMA_LEVEL1 ((__u32) 0x00000001) #define SND_AUDIOMODE_WMA_LEVEL2 ((__u32) 0x00000002) @@ -326,6 +331,33 @@ struct snd_dec_flac { __u16 reserved; } __attribute__((packed, aligned(4))); +struct snd_dec_wma { + __u32 encoder_option; + __u32 adv_encoder_option; + __u32 adv_encoder_option2; + __u32 reserved; +} __attribute__((packed, aligned(4))); + +struct snd_dec_alac { + __u32 frame_length; + __u8 compatible_version; + __u8 pb; + __u8 mb; + __u8 kb; + __u32 max_run; + __u32 max_frame_bytes; +} __attribute__((packed, aligned(4))); + +struct snd_dec_ape { + __u16 compatible_version; + __u16 compression_level; + __u32 format_flags; + __u32 blocks_per_frame; + __u32 final_frame_blocks; + __u32 total_frames; + __u32 seek_table_present; +} __attribute__((packed, aligned(4))); + union snd_codec_options { struct snd_enc_wma wma; struct snd_enc_vorbis vorbis; @@ -333,6 +365,9 @@ union snd_codec_options { struct snd_enc_flac flac; struct snd_enc_generic generic; struct snd_dec_flac flac_d; + struct snd_dec_wma wma_d; + struct snd_dec_alac alac_d; + struct snd_dec_ape ape_d; } __attribute__((packed, aligned(4))); /** struct snd_codec_desc - description of codec capabilities diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h index c0ef1643c753..5995b79d6df1 100644 --- a/include/uapi/sound/sof/abi.h +++ b/include/uapi/sound/sof/abi.h @@ -26,7 +26,7 @@ /* SOF ABI version major, minor and patch numbers */ #define SOF_ABI_MAJOR 3 -#define SOF_ABI_MINOR 12 +#define SOF_ABI_MINOR 13 #define SOF_ABI_PATCH 0 /* SOF ABI version number. Format within 32bit word is MMmmmppp */ diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index a86c95d89824..e81083e1bc68 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -38,7 +38,7 @@ int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct dma_slave_config config; int ret; - dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); if (!dma_params) return 0; @@ -47,7 +47,7 @@ int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, return ret; snd_dmaengine_pcm_set_config_from_dai_data(substream, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), + snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream), &config); ret = dmaengine_slave_config(chan, &config); @@ -95,7 +95,7 @@ int pxa2xx_pcm_open(struct snd_pcm_substream *substream) runtime->hw = pxa2xx_pcm_hardware; - dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); if (!dma_params) return 0; @@ -120,7 +120,7 @@ int pxa2xx_pcm_open(struct snd_pcm_substream *substream) return ret; return snd_dmaengine_pcm_open( - substream, dma_request_slave_channel(rtd->cpu_dai->dev, + substream, dma_request_slave_channel(asoc_rtd_to_cpu(rtd, 0)->dev, dma_params->chan_name)); } EXPORT_SYMBOL(pxa2xx_pcm_open); diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 9de1c9a0173e..509290f2efa8 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -488,6 +488,48 @@ out: } #endif /* !COMPR_CODEC_CAPS_OVERFLOW */ +int snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size) +{ + struct snd_dma_buffer *dmab; + int ret; + + if (snd_BUG_ON(!(stream) || !(stream)->runtime)) + return -EINVAL; + dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); + if (!dmab) + return -ENOMEM; + dmab->dev = stream->dma_buffer.dev; + ret = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab); + if (ret < 0) { + kfree(dmab); + return ret; + } + + snd_compr_set_runtime_buffer(stream, dmab); + stream->runtime->dma_bytes = size; + return 1; +} +EXPORT_SYMBOL(snd_compr_malloc_pages); + +int snd_compr_free_pages(struct snd_compr_stream *stream) +{ + struct snd_compr_runtime *runtime = stream->runtime; + + if (snd_BUG_ON(!(stream) || !(stream)->runtime)) + return -EINVAL; + if (runtime->dma_area == NULL) + return 0; + if (runtime->dma_buffer_p != &stream->dma_buffer) { + /* It's a newly allocated buffer. Release it now. */ + snd_dma_free_pages(runtime->dma_buffer_p); + kfree(runtime->dma_buffer_p); + } + + snd_compr_set_runtime_buffer(stream, NULL); + return 0; +} +EXPORT_SYMBOL(snd_compr_free_pages); + /* revisit this with snd_pcm_preallocate_xxx */ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, struct snd_compr_params *params) diff --git a/sound/core/device.c b/sound/core/device.c index cdc5af526739..bf0b04a7ee79 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -237,3 +237,24 @@ void snd_device_free_all(struct snd_card *card) list_for_each_entry_safe_reverse(dev, next, &card->devices, list) __snd_device_free(dev); } + +/** + * snd_device_get_state - Get the current state of the given device + * @card: the card instance + * @device_data: the data pointer to release + * + * Returns the current state of the given device object. For the valid + * device, either @SNDRV_DEV_BUILD, @SNDRV_DEV_REGISTERED or + * @SNDRV_DEV_DISCONNECTED is returned. + * Or for a non-existing device, -1 is returned as an error. + */ +int snd_device_get_state(struct snd_card *card, void *device_data) +{ + struct snd_device *dev; + + dev = look_for_dev(card, device_data); + if (dev) + return dev->state; + return -1; +} +EXPORT_SYMBOL_GPL(snd_device_get_state); diff --git a/sound/core/info.c b/sound/core/info.c index ca87ae4c30ba..8c6bc5241df5 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -604,7 +604,7 @@ int snd_info_card_free(struct snd_card *card) */ int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) { - int c = -1; + int c; if (snd_BUG_ON(!buffer || !buffer->buffer)) return 1; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 13db77771f0f..930def8201f4 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -884,20 +884,17 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) sformat = snd_pcm_plug_slave_format(format, sformat_mask); if ((__force int)sformat < 0 || - !snd_mask_test(sformat_mask, (__force int)sformat)) { - for (sformat = (__force snd_pcm_format_t)0; - (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; - sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) { - if (snd_mask_test(sformat_mask, (__force int)sformat) && + !snd_mask_test_format(sformat_mask, sformat)) { + pcm_for_each_format(sformat) { + if (snd_mask_test_format(sformat_mask, sformat) && snd_pcm_oss_format_to(sformat) >= 0) - break; - } - if ((__force int)sformat > (__force int)SNDRV_PCM_FORMAT_LAST) { - pcm_dbg(substream->pcm, "Cannot find a format!!!\n"); - err = -EINVAL; - goto failure; + goto format_found; } + pcm_dbg(substream->pcm, "Cannot find a format!!!\n"); + err = -EINVAL; + goto failure; } + format_found: err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0); if (err < 0) goto failure; @@ -1220,8 +1217,10 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const if (ret < 0) break; } + mutex_unlock(&runtime->oss.params_lock); ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true, frames, in_kernel); + mutex_lock(&runtime->oss.params_lock); if (ret != -EPIPE && ret != -ESTRPIPE) break; /* test, if we can't store new data, because the stream */ @@ -1257,8 +1256,10 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p ret = snd_pcm_oss_capture_position_fixup(substream, &delay); if (ret < 0) break; + mutex_unlock(&runtime->oss.params_lock); ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true, frames, in_kernel); + mutex_lock(&runtime->oss.params_lock); if (ret == -EPIPE) { if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 752d078908e9..fbda4ebf38b3 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -196,82 +196,74 @@ int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin) return 0; } -snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames) +static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug, + snd_pcm_sframes_t frames) { - struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; - int stream; + struct snd_pcm_plugin *plugin, *plugin_next; - if (snd_BUG_ON(!plug)) - return -ENXIO; - if (drv_frames == 0) - return 0; - stream = snd_pcm_plug_stream(plug); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - plugin = snd_pcm_plug_last(plug); - while (plugin && drv_frames > 0) { - if (drv_frames > plugin->buf_frames) - drv_frames = plugin->buf_frames; - plugin_prev = plugin->prev; - if (plugin->src_frames) - drv_frames = plugin->src_frames(plugin, drv_frames); - plugin = plugin_prev; + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + plugin_next = plugin->next; + if (plugin->dst_frames) { + frames = plugin->dst_frames(plugin, frames); + if (frames < 0) + return frames; } - } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { - plugin = snd_pcm_plug_first(plug); - while (plugin && drv_frames > 0) { - plugin_next = plugin->next; - if (plugin->dst_frames) - drv_frames = plugin->dst_frames(plugin, drv_frames); - if (drv_frames > plugin->buf_frames) - drv_frames = plugin->buf_frames; - plugin = plugin_next; + if (frames > plugin->buf_frames) + frames = plugin->buf_frames; + plugin = plugin_next; + } + return frames; +} + +static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug, + snd_pcm_sframes_t frames) +{ + struct snd_pcm_plugin *plugin, *plugin_prev; + + plugin = snd_pcm_plug_last(plug); + while (plugin && frames > 0) { + if (frames > plugin->buf_frames) + frames = plugin->buf_frames; + plugin_prev = plugin->prev; + if (plugin->src_frames) { + frames = plugin->src_frames(plugin, frames); + if (frames < 0) + return frames; } - } else + plugin = plugin_prev; + } + return frames; +} + +snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames) +{ + if (snd_BUG_ON(!plug)) + return -ENXIO; + switch (snd_pcm_plug_stream(plug)) { + case SNDRV_PCM_STREAM_PLAYBACK: + return calc_src_frames(plug, drv_frames); + case SNDRV_PCM_STREAM_CAPTURE: + return calc_dst_frames(plug, drv_frames); + default: snd_BUG(); - return drv_frames; + return -EINVAL; + } } snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames) { - struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; - snd_pcm_sframes_t frames; - int stream; - if (snd_BUG_ON(!plug)) return -ENXIO; - if (clt_frames == 0) - return 0; - frames = clt_frames; - stream = snd_pcm_plug_stream(plug); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - plugin = snd_pcm_plug_first(plug); - while (plugin && frames > 0) { - plugin_next = plugin->next; - if (plugin->dst_frames) { - frames = plugin->dst_frames(plugin, frames); - if (frames < 0) - return frames; - } - if (frames > plugin->buf_frames) - frames = plugin->buf_frames; - plugin = plugin_next; - } - } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { - plugin = snd_pcm_plug_last(plug); - while (plugin) { - if (frames > plugin->buf_frames) - frames = plugin->buf_frames; - plugin_prev = plugin->prev; - if (plugin->src_frames) { - frames = plugin->src_frames(plugin, frames); - if (frames < 0) - return frames; - } - plugin = plugin_prev; - } - } else + switch (snd_pcm_plug_stream(plug)) { + case SNDRV_PCM_STREAM_PLAYBACK: + return calc_dst_frames(plug, clt_frames); + case SNDRV_PCM_STREAM_CAPTURE: + return calc_src_frames(plug, clt_frames); + default: snd_BUG(); - return frames; + return -EINVAL; + } } static int snd_pcm_plug_formats(const struct snd_mask *mask, diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c index 7cd09cef6961..d381f4c967c9 100644 --- a/sound/core/oss/rate.c +++ b/sound/core/oss/rate.c @@ -47,7 +47,7 @@ struct rate_priv { unsigned int pos; rate_f func; snd_pcm_sframes_t old_src_frames, old_dst_frames; - struct rate_channel channels[0]; + struct rate_channel channels[]; }; static void rate_init(struct snd_pcm_plugin *plugin) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index a141a301369f..b6d2331a82f7 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1019,7 +1019,7 @@ static ssize_t show_pcm_class(struct device *dev, str = "none"; else str = strs[pcm->dev_class]; - return snprintf(buf, PAGE_SIZE, "%s\n", str); + return sprintf(buf, "%s\n", str); } static DEVICE_ATTR(pcm_class, 0444, show_pcm_class, NULL); diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 5749a8a49784..4d059ff2b2e4 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -240,6 +240,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct snd_pcm_runtime *runtime = substream->runtime; struct dma_tx_state state; enum dma_status status; unsigned int buf_size; @@ -250,9 +251,12 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) buf_size = snd_pcm_lib_buffer_bytes(substream); if (state.residue > 0 && state.residue <= buf_size) pos = buf_size - state.residue; + + runtime->delay = bytes_to_frames(runtime, + state.in_flight_bytes); } - return bytes_to_frames(substream->runtime, pos); + return bytes_to_frames(runtime, pos); } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); @@ -426,7 +430,7 @@ int snd_dmaengine_pcm_refine_runtime_hwparams( * default assumption is that it supports 1, 2 and 4 bytes * widths. */ - for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) { + pcm_for_each_format(i) { int bits = snd_pcm_format_physical_width(i); /* diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index a6a541511534..257d412eac5d 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -42,6 +42,11 @@ struct pcm_format_data { /* we do lots of calculations on snd_pcm_format_t; shut up sparse */ #define INT __force int +static bool valid_format(snd_pcm_format_t format) +{ + return (INT)format >= 0 && (INT)format <= (INT)SNDRV_PCM_FORMAT_LAST; +} + static const struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { [SNDRV_PCM_FORMAT_S8] = { .width = 8, .phys = 8, .le = -1, .signd = 1, @@ -259,7 +264,7 @@ static const struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = int snd_pcm_format_signed(snd_pcm_format_t format) { int val; - if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) + if (!valid_format(format)) return -EINVAL; if ((val = pcm_formats[(INT)format].signd) < 0) return -EINVAL; @@ -307,7 +312,7 @@ EXPORT_SYMBOL(snd_pcm_format_linear); int snd_pcm_format_little_endian(snd_pcm_format_t format) { int val; - if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) + if (!valid_format(format)) return -EINVAL; if ((val = pcm_formats[(INT)format].le) < 0) return -EINVAL; @@ -343,7 +348,7 @@ EXPORT_SYMBOL(snd_pcm_format_big_endian); int snd_pcm_format_width(snd_pcm_format_t format) { int val; - if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) + if (!valid_format(format)) return -EINVAL; if ((val = pcm_formats[(INT)format].width) == 0) return -EINVAL; @@ -361,7 +366,7 @@ EXPORT_SYMBOL(snd_pcm_format_width); int snd_pcm_format_physical_width(snd_pcm_format_t format) { int val; - if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) + if (!valid_format(format)) return -EINVAL; if ((val = pcm_formats[(INT)format].phys) == 0) return -EINVAL; @@ -394,7 +399,7 @@ EXPORT_SYMBOL(snd_pcm_format_size); */ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) { - if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) + if (!valid_format(format)) return NULL; if (! pcm_formats[(INT)format].phys) return NULL; @@ -418,7 +423,7 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int unsigned char *dst; const unsigned char *pat; - if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) + if (!valid_format(format)) return -EINVAL; if (samples == 0) return 0; @@ -474,32 +479,32 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int EXPORT_SYMBOL(snd_pcm_format_set_silence); /** - * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields - * @runtime: the runtime instance + * snd_pcm_hw_limit_rates - determine rate_min/rate_max fields + * @hw: the pcm hw instance * * Determines the rate_min and rate_max fields from the rates bits of - * the given runtime->hw. + * the given hw. * * Return: Zero if successful. */ -int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) +int snd_pcm_hw_limit_rates(struct snd_pcm_hardware *hw) { int i; for (i = 0; i < (int)snd_pcm_known_rates.count; i++) { - if (runtime->hw.rates & (1 << i)) { - runtime->hw.rate_min = snd_pcm_known_rates.list[i]; + if (hw->rates & (1 << i)) { + hw->rate_min = snd_pcm_known_rates.list[i]; break; } } for (i = (int)snd_pcm_known_rates.count - 1; i >= 0; i--) { - if (runtime->hw.rates & (1 << i)) { - runtime->hw.rate_max = snd_pcm_known_rates.list[i]; + if (hw->rates & (1 << i)) { + hw->rate_max = snd_pcm_known_rates.list[i]; break; } } return 0; } -EXPORT_SYMBOL(snd_pcm_limit_hw_rates); +EXPORT_SYMBOL(snd_pcm_hw_limit_rates); /** * snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index cbdf061667fa..aef860256278 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -228,6 +228,9 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, return err; } +/* macro for simplified cast */ +#define PARAM_MASK_BIT(b) (1U << (__force int)(b)) + static bool hw_support_mmap(struct snd_pcm_substream *substream) { if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP)) @@ -257,7 +260,7 @@ static int constrain_mask_params(struct snd_pcm_substream *substream, return -EINVAL; /* This parameter is not requested to change by a caller. */ - if (!(params->rmask & (1 << k))) + if (!(params->rmask & PARAM_MASK_BIT(k))) continue; if (trace_hw_mask_param_enabled()) @@ -271,7 +274,7 @@ static int constrain_mask_params(struct snd_pcm_substream *substream, /* Set corresponding flag so that the caller gets it. */ trace_hw_mask_param(substream, k, 0, &old_mask, m); - params->cmask |= 1 << k; + params->cmask |= PARAM_MASK_BIT(k); } return 0; @@ -293,7 +296,7 @@ static int constrain_interval_params(struct snd_pcm_substream *substream, return -EINVAL; /* This parameter is not requested to change by a caller. */ - if (!(params->rmask & (1 << k))) + if (!(params->rmask & PARAM_MASK_BIT(k))) continue; if (trace_hw_interval_param_enabled()) @@ -307,7 +310,7 @@ static int constrain_interval_params(struct snd_pcm_substream *substream, /* Set corresponding flag so that the caller gets it. */ trace_hw_interval_param(substream, k, 0, &old_interval, i); - params->cmask |= 1 << k; + params->cmask |= PARAM_MASK_BIT(k); } return 0; @@ -349,7 +352,7 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, * have 0 so that the parameters are never changed anymore. */ for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) - vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; + vstamps[k] = (params->rmask & PARAM_MASK_BIT(k)) ? 1 : 0; /* Due to the above design, actual sequence number starts at 2. */ stamp = 2; @@ -417,7 +420,7 @@ retry: hw_param_interval(params, r->var)); } - params->cmask |= (1 << r->var); + params->cmask |= PARAM_MASK_BIT(r->var); vstamps[r->var] = stamp; again = true; } @@ -486,9 +489,9 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, params->info = 0; params->fifo_size = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) params->msbits = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_RATE)) { params->rate_num = 0; params->rate_den = 0; } @@ -2293,21 +2296,21 @@ static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params, static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - unsigned int k; + snd_pcm_format_t k; const struct snd_interval *i = hw_param_interval_c(params, rule->deps[0]); struct snd_mask m; struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); snd_mask_any(&m); - for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + pcm_for_each_format(k) { int bits; - if (! snd_mask_test(mask, k)) + if (!snd_mask_test_format(mask, k)) continue; bits = snd_pcm_format_physical_width(k); if (bits <= 0) continue; /* ignore invalid formats */ if ((unsigned)bits < i->min || (unsigned)bits > i->max) - snd_mask_reset(&m, k); + snd_mask_reset(&m, (__force unsigned)k); } return snd_mask_refine(mask, &m); } @@ -2316,14 +2319,15 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval t; - unsigned int k; + snd_pcm_format_t k; + t.min = UINT_MAX; t.max = 0; t.openmin = 0; t.openmax = 0; - for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + pcm_for_each_format(k) { int bits; - if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k)) + if (!snd_mask_test_format(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k)) continue; bits = snd_pcm_format_physical_width(k); if (bits <= 0) @@ -2505,16 +2509,16 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) unsigned int mask = 0; if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) - mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; + mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) - mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); if (hw_support_mmap(substream)) { if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) - mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) - mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED; + mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED); if (hw->info & SNDRV_PCM_INFO_COMPLEX) - mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; + mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_COMPLEX); } err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); if (err < 0) @@ -2524,7 +2528,8 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) if (err < 0) return err; - err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, + PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD)); if (err < 0) return err; diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index d78a27271d6d..251eaf1152e2 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -118,7 +118,7 @@ struct loopback_cable { struct loopback_setup { unsigned int notify: 1; unsigned int rate_shift; - unsigned int format; + snd_pcm_format_t format; unsigned int rate; unsigned int channels; struct snd_ctl_elem_id active_id; @@ -1432,7 +1432,7 @@ static int loopback_format_info(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST; + uinfo->value.integer.max = (__force int)SNDRV_PCM_FORMAT_LAST; uinfo->value.integer.step = 1; return 0; } @@ -1443,7 +1443,7 @@ static int loopback_format_get(struct snd_kcontrol *kcontrol, struct loopback *loopback = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = - loopback->setup[kcontrol->id.subdevice] + (__force int)loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].format; return 0; } diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 02ac3f4e0c02..b5486de08b97 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -901,10 +901,10 @@ static int snd_card_dummy_new_mixer(struct snd_dummy *dummy) static void print_formats(struct snd_dummy *dummy, struct snd_info_buffer *buffer) { - int i; + snd_pcm_format_t i; - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - if (dummy->pcm_hw.formats & (1ULL << i)) + pcm_for_each_format(i) { + if (dummy->pcm_hw.formats & pcm_format_to_bits(i)) snd_iprintf(buffer, " %s", snd_pcm_format_name(i)); } } diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 976d8cb9a34f..2c8e3392a490 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -509,7 +509,7 @@ MODULE_DEVICE_TABLE(ieee1394, bebob_id_table); static struct fw_driver bebob_driver = { .driver = { .owner = THIS_MODULE, - .name = "snd-bebob", + .name = KBUILD_MODNAME, .bus = &fw_bus_type, }, .probe = bebob_probe, diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index 1f5fc0e7c024..c84b913a9fe0 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -192,7 +192,7 @@ MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table); static struct fw_driver dg00x_driver = { .driver = { .owner = THIS_MODULE, - .name = "snd-firewire-digi00x", + .name = KBUILD_MODNAME, .bus = &fw_bus_type, }, .probe = snd_dg00x_probe, diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index f5a016560eb8..b62a4fd22407 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -224,7 +224,7 @@ MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table); static struct fw_driver ff_driver = { .driver = { .owner = THIS_MODULE, - .name = "snd-fireface", + .name = KBUILD_MODNAME, .bus = &fw_bus_type, }, .probe = snd_ff_probe, diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index 134fc9ee26b9..b1cc013a3540 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -362,7 +362,7 @@ MODULE_DEVICE_TABLE(ieee1394, efw_id_table); static struct fw_driver efw_driver = { .driver = { .owner = THIS_MODULE, - .name = "snd-fireworks", + .name = KBUILD_MODNAME, .bus = &fw_bus_type, }, .probe = efw_probe, diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c index c29a97f6f638..6f38335fe10b 100644 --- a/sound/firewire/tascam/tascam-hwdep.c +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -17,6 +17,7 @@ static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf, long count, loff_t *offset) + __releases(&tscm->lock) { struct snd_firewire_event_lock_status event = { .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, @@ -36,6 +37,7 @@ static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf, static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf, long remained, loff_t *offset) + __releases(&tscm->lock) { char __user *pos = buf; unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL; diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index addc464503bc..5dac0d9fc58e 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -224,7 +224,7 @@ MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table); static struct fw_driver tscm_driver = { .driver = { .owner = THIS_MODULE, - .name = "snd-firewire-tascam", + .name = KBUILD_MODNAME, .bus = &fw_bus_type, }, .probe = snd_tscm_probe, diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 9a526aeef8da..e3119f5cb0d5 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -204,7 +204,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name); */ int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size) { - return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n", + return scnprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n", codec->vendor_id, codec->revision_id, codec->type); } EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias); diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c index e377ac93f37f..8e8257c574b0 100644 --- a/sound/isa/sb/emu8000_pcm.c +++ b/sound/isa/sb/emu8000_pcm.c @@ -435,7 +435,7 @@ enum { #define LOOP_WRITE(rec, offset, _buf, count, mode) \ do { \ struct snd_emu8000 *emu = (rec)->emu; \ - unsigned short *buf = (unsigned short *)(_buf); \ + unsigned short *buf = (__force unsigned short *)(_buf); \ snd_emu8000_write_wait(emu, 1); \ EMU8000_SMALW_WRITE(emu, offset); \ while (count > 0) { \ @@ -492,7 +492,7 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs, #define LOOP_WRITE(rec, pos, _buf, count, mode) \ do { \ struct snd_emu8000 *emu = rec->emu; \ - unsigned short *buf = (unsigned short *)(_buf); \ + unsigned short *buf = (__force unsigned short *)(_buf); \ snd_emu8000_write_wait(emu, 1); \ EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \ if (rec->voices > 1) \ diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 4f524a9dbbca..4462375d2d82 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1070,7 +1070,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream, { struct snd_ali *codec = snd_pcm_substream_chip(substream); struct snd_pcm_substream *s; - unsigned int what, whati, capture_flag; + unsigned int what, whati; struct snd_ali_voice *pvoice, *evoice; unsigned int val; int do_start; @@ -1088,7 +1088,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream, return -EINVAL; } - what = whati = capture_flag = 0; + what = whati = 0; snd_pcm_group_for_each_entry(s, substream) { if ((struct snd_ali *) snd_pcm_substream_chip(s) == codec) { pvoice = s->runtime->private_data; @@ -1110,8 +1110,6 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream, evoice->running = 0; } snd_pcm_trigger_done(s, substream); - if (pvoice->mode) - capture_flag = 1; } } spin_lock(&codec->reg_lock); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index a89a7e603ca8..6ff581733a19 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1789,6 +1789,7 @@ int snd_emu10k1_create(struct snd_card *card, int idx, err; int is_audigy; size_t page_table_size; + __le32 *pgtbl; unsigned int silent_page; const struct snd_emu_chip_details *c; static const struct snd_device_ops ops = { @@ -2009,8 +2010,9 @@ int snd_emu10k1_create(struct snd_card *card, /* Clear silent pages and set up pointers */ memset(emu->silent_page.area, 0, emu->silent_page.bytes); silent_page = emu->silent_page.addr << emu->address_mode; + pgtbl = (__le32 *)emu->ptb_pages.area; for (idx = 0; idx < (emu->address_mode ? MAXPAGES1 : MAXPAGES0); idx++) - ((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx); + pgtbl[idx] = cpu_to_le32(silent_page | idx); /* set up voice indices */ for (idx = 0; idx < NUM_G; idx++) { diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index bd48335d09d7..e1d3082a4fe9 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -184,6 +184,7 @@ comment "Set to Y if you want auto-loading the codec driver" config SND_HDA_CODEC_CA0132_DSP bool "Support new DSP code for CA0132 codec" depends on SND_HDA_CODEC_CA0132 + default y select SND_HDA_DSP_LOADER select FW_LOADER help diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 53e7732ef752..a34a2c9f4bcf 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -88,7 +88,7 @@ struct hda_conn_list { struct list_head list; int len; hda_nid_t nid; - hda_nid_t conns[0]; + hda_nid_t conns[]; }; /* look up the cached results */ diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 2609e391ce54..9765652a73d7 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -373,7 +373,7 @@ static int azx_get_sync_time(ktime_t *device, u32 wallclk_ctr, wallclk_cycles; bool direction; u32 dma_select; - u32 timeout = 200; + u32 timeout; u32 retry_count = 0; runtime = substream->runtime; diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index ded8bc07d755..34fe753a46fb 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1180,6 +1180,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI), SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI), SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI), + SND_PCI_QUIRK(0x3842, 0x1038, "EVGA X99 Classified", QUIRK_R3DI), SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D), SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5), {} @@ -2698,7 +2699,7 @@ struct dsp_image_seg { u32 magic; u32 chip_addr; u32 count; - u32 data[0]; + u32 data[]; }; static const u32 g_magic_value = 0x4c46584d; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5119a9ae3d8a..bb287a916dae 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -154,7 +154,6 @@ struct hdmi_spec { struct hda_multi_out multiout; struct hda_pcm_stream pcm_playback; - bool use_jack_detect; /* jack detection enabled */ bool use_acomp_notifier; /* use eld_notify callback for hotplug */ bool acomp_registered; /* audio component registered in this driver */ struct drm_audio_component_audio_ops drm_audio_ops; @@ -753,7 +752,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, * Unsolicited events */ -static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); +static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid, int dev_id) @@ -764,8 +763,7 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid, if (pin_idx < 0) return; mutex_lock(&spec->pcm_lock); - if (hdmi_present_sense(get_pin(spec, pin_idx), 1)) - snd_hda_jack_report_sync(codec); + hdmi_present_sense(get_pin(spec, pin_idx), 1); mutex_unlock(&spec->pcm_lock); } @@ -779,21 +777,9 @@ static void jack_callback(struct hda_codec *codec, check_presence_and_report(codec, jack->nid, jack->dev_id); } -static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) +static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res, + struct hda_jack_tbl *jack) { - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - struct hda_jack_tbl *jack; - - if (codec->dp_mst) { - int dev_entry = - (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT; - - jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry); - } else { - jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0); - } - if (!jack) - return; jack->jack_dirty = 1; codec_dbg(codec, @@ -853,7 +839,7 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) } if (subtag == 0) - hdmi_intrinsic_event(codec, res); + hdmi_intrinsic_event(codec, res, jack); else hdmi_non_intrinsic_event(codec, res); } @@ -1480,21 +1466,60 @@ static void hdmi_pcm_reset_pin(struct hdmi_spec *spec, per_pin->channels = 0; } +static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + + if (per_pin->pcm_idx >= 0) + return spec->pcm_rec[per_pin->pcm_idx].jack; + else + return NULL; +} + /* update per_pin ELD from the given new ELD; * setup info frame and notification accordingly + * also notify ELD kctl and report jack status changes */ -static bool update_eld(struct hda_codec *codec, +static void update_eld(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, - struct hdmi_eld *eld) + struct hdmi_eld *eld, + int repoll) { struct hdmi_eld *pin_eld = &per_pin->sink_eld; struct hdmi_spec *spec = codec->spec; + struct snd_jack *pcm_jack; bool old_eld_valid = pin_eld->eld_valid; bool eld_changed; int pcm_idx; + if (eld->eld_valid) { + if (eld->eld_size <= 0 || + snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer, + eld->eld_size) < 0) { + eld->eld_valid = false; + if (repoll) { + schedule_delayed_work(&per_pin->work, + msecs_to_jiffies(300)); + return; + } + } + } + + if (!eld->eld_valid || eld->eld_size <= 0) { + eld->eld_valid = false; + eld->eld_size = 0; + } + /* for monitor disconnection, save pcm_idx firstly */ pcm_idx = per_pin->pcm_idx; + + /* + * pcm_idx >=0 before update_eld() means it is in monitor + * disconnected event. Jack must be fetched before update_eld(). + */ + pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); + if (spec->dyn_pcm_assign) { if (eld->eld_valid) { hdmi_attach_hda_pcm(spec, per_pin); @@ -1509,6 +1534,8 @@ static bool update_eld(struct hda_codec *codec, */ if (pcm_idx == -1) pcm_idx = per_pin->pcm_idx; + if (!pcm_jack) + pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); if (eld->eld_valid) snd_hdmi_show_eld(codec, &eld->info); @@ -1547,42 +1574,17 @@ static bool update_eld(struct hda_codec *codec, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id); - return eld_changed; -} -static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hdmi_spec *spec = codec->spec; - struct snd_jack *jack = NULL; - struct hda_jack_tbl *jack_tbl; - - /* if !dyn_pcm_assign, get jack from hda_jack_tbl - * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not - * NULL even after snd_hda_jack_tbl_clear() is called to - * free snd_jack. This may cause access invalid memory - * when calling snd_jack_report - */ - if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign) { - jack = spec->pcm_rec[per_pin->pcm_idx].jack; - } else if (!spec->dyn_pcm_assign) { - /* - * jack tbl doesn't support DP MST - * DP MST will use dyn_pcm_assign, - * so DP MST will never come here - */ - jack_tbl = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid, - per_pin->dev_id); - if (jack_tbl) - jack = jack_tbl->jack; - } - return jack; + if (eld_changed && pcm_jack) + snd_jack_report(pcm_jack, + (eld->monitor_present && eld->eld_valid) ? + SND_JACK_AVOUT : 0); } + /* update ELD and jack state via HD-audio verbs */ -static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, +static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, int repoll) { - struct hda_jack_tbl *jack; struct hda_codec *codec = per_pin->codec; struct hdmi_spec *spec = codec->spec; struct hdmi_eld *eld = &spec->temp_eld; @@ -1597,9 +1599,11 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, * the unsolicited response to avoid custom WARs. */ int present; - bool ret; - bool do_repoll = false; - struct snd_jack *pcm_jack = NULL; + int ret; + + ret = snd_hda_power_up_pm(codec); + if (ret < 0 && pm_runtime_suspended(hda_codec_dev(codec))) + goto out; present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id); @@ -1618,62 +1622,12 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, if (spec->ops.pin_get_eld(codec, pin_nid, dev_id, eld->eld_buffer, &eld->eld_size) < 0) eld->eld_valid = false; - else { - if (snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer, - eld->eld_size) < 0) - eld->eld_valid = false; - } - if (!eld->eld_valid && repoll) - do_repoll = true; } - if (do_repoll) { - schedule_delayed_work(&per_pin->work, msecs_to_jiffies(300)); - } else { - /* - * pcm_idx >=0 before update_eld() means it is in monitor - * disconnected event. Jack must be fetched before - * update_eld(). - */ - pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); - update_eld(codec, per_pin, eld); - if (!pcm_jack) - pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); - } - - ret = !repoll || !eld->monitor_present || eld->eld_valid; - - jack = snd_hda_jack_tbl_get_mst(codec, pin_nid, per_pin->dev_id); - if (jack) { - jack->block_report = !ret; - jack->pin_sense = (eld->monitor_present && eld->eld_valid) ? - AC_PINSENSE_PRESENCE : 0; - - if (spec->dyn_pcm_assign && pcm_jack && !do_repoll) { - int state = 0; - - if (jack->pin_sense & AC_PINSENSE_PRESENCE) - state = SND_JACK_AVOUT; - snd_jack_report(pcm_jack, state); - } - - /* - * snd_hda_jack_pin_sense() call at the beginning of this - * function, updates jack->pins_sense and clears - * jack->jack_dirty, therefore snd_hda_jack_report_sync() will - * not override the jack->pin_sense. - * - * snd_hda_jack_report_sync() is superfluous for dyn_pcm_assign - * case. The jack->pin_sense update was already performed, and - * hda_jack->jack is NULL for dyn_pcm_assign. - * - * Don't call snd_hda_jack_report_sync() for - * dyn_pcm_assign. - */ - ret = ret && !spec->dyn_pcm_assign; - } + update_eld(codec, per_pin, eld, repoll); mutex_unlock(&per_pin->lock); - return ret; + out: + snd_hda_power_down_pm(codec); } /* update ELD and jack state via audio component */ @@ -1682,64 +1636,25 @@ static void sync_eld_via_acomp(struct hda_codec *codec, { struct hdmi_spec *spec = codec->spec; struct hdmi_eld *eld = &spec->temp_eld; - struct snd_jack *jack = NULL; - bool changed; - int size; mutex_lock(&per_pin->lock); eld->monitor_present = false; - size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, + eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, per_pin->dev_id, &eld->monitor_present, eld->eld_buffer, ELD_MAX_SIZE); - if (size > 0) { - size = min(size, ELD_MAX_SIZE); - if (snd_hdmi_parse_eld(codec, &eld->info, - eld->eld_buffer, size) < 0) - size = -EINVAL; - } - - if (size > 0) { - eld->eld_valid = true; - eld->eld_size = size; - } else { - eld->eld_valid = false; - eld->eld_size = 0; - } - - /* pcm_idx >=0 before update_eld() means it is in monitor - * disconnected event. Jack must be fetched before update_eld() - */ - jack = pin_idx_to_pcm_jack(codec, per_pin); - changed = update_eld(codec, per_pin, eld); - if (jack == NULL) - jack = pin_idx_to_pcm_jack(codec, per_pin); - if (changed && jack) - snd_jack_report(jack, - (eld->monitor_present && eld->eld_valid) ? - SND_JACK_AVOUT : 0); + eld->eld_valid = (eld->eld_size > 0); + update_eld(codec, per_pin, eld, 0); mutex_unlock(&per_pin->lock); } -static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) +static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) { struct hda_codec *codec = per_pin->codec; - int ret; - /* no temporary power up/down needed for component notifier */ - if (!codec_has_acomp(codec)) { - ret = snd_hda_power_up_pm(codec); - if (ret < 0 && pm_runtime_suspended(hda_codec_dev(codec))) { - snd_hda_power_down_pm(codec); - return false; - } - ret = hdmi_present_sense_via_verbs(per_pin, repoll); - snd_hda_power_down_pm(codec); - } else { + if (!codec_has_acomp(codec)) + hdmi_present_sense_via_verbs(per_pin, repoll); + else sync_eld_via_acomp(codec, per_pin); - ret = false; /* don't call snd_hda_jack_report_sync() */ - } - - return ret; } static void hdmi_repoll_eld(struct work_struct *work) @@ -1759,8 +1674,7 @@ static void hdmi_repoll_eld(struct work_struct *work) per_pin->repoll_count = 0; mutex_lock(&spec->pcm_lock); - if (hdmi_present_sense(per_pin, per_pin->repoll_count)) - snd_hda_jack_report_sync(per_pin->codec); + hdmi_present_sense(per_pin, per_pin->repoll_count); mutex_unlock(&spec->pcm_lock); } @@ -2206,15 +2120,23 @@ static void free_hdmi_jack_priv(struct snd_jack *jack) pcm->jack = NULL; } -static int add_hdmi_jack_kctl(struct hda_codec *codec, - struct hdmi_spec *spec, - int pcm_idx, - const char *name) +static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) { + char hdmi_str[32] = "HDMI/DP"; + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pcm_idx); struct snd_jack *jack; + int pcmdev = get_pcm_rec(spec, pcm_idx)->device; int err; - err = snd_jack_new(codec->card, name, SND_JACK_AVOUT, &jack, + if (pcmdev > 0) + sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); + if (!spec->dyn_pcm_assign && + !is_jack_detectable(codec, per_pin->pin_nid)) + strncat(hdmi_str, " Phantom", + sizeof(hdmi_str) - strlen(hdmi_str) - 1); + + err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack, true, false); if (err < 0) return err; @@ -2225,48 +2147,6 @@ static int add_hdmi_jack_kctl(struct hda_codec *codec, return 0; } -static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) -{ - char hdmi_str[32] = "HDMI/DP"; - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - struct hda_jack_tbl *jack; - int pcmdev = get_pcm_rec(spec, pcm_idx)->device; - bool phantom_jack; - int ret; - - if (pcmdev > 0) - sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); - - if (spec->dyn_pcm_assign) - return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str); - - /* for !dyn_pcm_assign, we still use hda_jack for compatibility */ - /* if !dyn_pcm_assign, it must be non-MST mode. - * This means pcms and pins are statically mapped. - * And pcm_idx is pin_idx. - */ - per_pin = get_pin(spec, pcm_idx); - phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid); - if (phantom_jack) - strncat(hdmi_str, " Phantom", - sizeof(hdmi_str) - strlen(hdmi_str) - 1); - ret = snd_hda_jack_add_kctl_mst(codec, per_pin->pin_nid, - per_pin->dev_id, hdmi_str, phantom_jack, - 0, NULL); - if (ret < 0) - return ret; - jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid, - per_pin->dev_id); - if (jack == NULL) - return 0; - /* assign jack->jack to pcm_rec[].jack to - * align with dyn_pcm_assign mode - */ - spec->pcm_rec[pcm_idx].jack = jack->jack; - return 0; -} - static int generic_hdmi_build_controls(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -2355,7 +2235,6 @@ static int generic_hdmi_init(struct hda_codec *codec) int pin_idx; mutex_lock(&spec->bind_lock); - spec->use_jack_detect = !codec->jackpoll_interval; for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); hda_nid_t pin_nid = per_pin->pin_nid; @@ -2365,12 +2244,8 @@ static int generic_hdmi_init(struct hda_codec *codec) hdmi_init_pin(codec, pin_nid); if (codec_has_acomp(codec)) continue; - if (spec->use_jack_detect) - snd_hda_jack_detect_enable(codec, pin_nid, dev_id); - else - snd_hda_jack_detect_enable_callback_mst(codec, pin_nid, - dev_id, - jack_callback); + snd_hda_jack_detect_enable_callback_mst(codec, pin_nid, dev_id, + jack_callback); } mutex_unlock(&spec->bind_lock); return 0; @@ -2532,12 +2407,6 @@ static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid, unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag); snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, val); - } else { - /* if no jack entry was defined beforehand, create a new one - * at need (i.e. only when notifier is cleared) - */ - if (!use_acomp) - snd_hda_jack_detect_enable(codec, nid, dev_id); } } @@ -2553,13 +2422,11 @@ static void generic_acomp_notifier_set(struct drm_audio_component *acomp, spec->use_acomp_notifier = use_acomp; spec->codec->relaxed_resume = use_acomp; /* reprogram each jack detection logic depending on the notifier */ - if (spec->use_jack_detect) { - for (i = 0; i < spec->num_pins; i++) - reprogram_jack_detect(spec->codec, - get_pin(spec, i)->pin_nid, - get_pin(spec, i)->dev_id, - use_acomp); - } + for (i = 0; i < spec->num_pins; i++) + reprogram_jack_detect(spec->codec, + get_pin(spec, i)->pin_nid, + get_pin(spec, i)->dev_id, + use_acomp); mutex_unlock(&spec->bind_lock); } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 63e1a56f705b..f66a48154a57 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -107,6 +107,7 @@ struct alc_spec { unsigned int done_hp_init:1; unsigned int no_shutup_pins:1; unsigned int ultra_low_power:1; + unsigned int has_hs_key:1; /* for PLL fix */ hda_nid_t pll_nid; @@ -367,7 +368,9 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0215: case 0x10ec0233: case 0x10ec0235: + case 0x10ec0236: case 0x10ec0255: + case 0x10ec0256: case 0x10ec0257: case 0x10ec0282: case 0x10ec0283: @@ -379,11 +382,6 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0300: alc_update_coef_idx(codec, 0x10, 1<<9, 0); break; - case 0x10ec0236: - case 0x10ec0256: - alc_write_coef_idx(codec, 0x36, 0x5757); - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - break; case 0x10ec0275: alc_update_coef_idx(codec, 0xe, 0, 1<<0); break; @@ -2982,6 +2980,107 @@ static int alc269_parse_auto_config(struct hda_codec *codec) return alc_parse_auto_config(codec, alc269_ignore, ssids); } +static const struct hda_jack_keymap alc_headset_btn_keymap[] = { + { SND_JACK_BTN_0, KEY_PLAYPAUSE }, + { SND_JACK_BTN_1, KEY_VOICECOMMAND }, + { SND_JACK_BTN_2, KEY_VOLUMEUP }, + { SND_JACK_BTN_3, KEY_VOLUMEDOWN }, + {} +}; + +static void alc_headset_btn_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + int report = 0; + + if (jack->unsol_res & (7 << 13)) + report |= SND_JACK_BTN_0; + + if (jack->unsol_res & (1 << 16 | 3 << 8)) + report |= SND_JACK_BTN_1; + + /* Volume up key */ + if (jack->unsol_res & (7 << 23)) + report |= SND_JACK_BTN_2; + + /* Volume down key */ + if (jack->unsol_res & (7 << 10)) + report |= SND_JACK_BTN_3; + + jack->jack->button_state = report; +} + +static void alc_disable_headset_jack_key(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->has_hs_key) + return; + + switch (codec->core.vendor_id) { + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_write_coef_idx(codec, 0x48, 0x0); + alc_update_coef_idx(codec, 0x49, 0x0045, 0x0); + alc_update_coef_idx(codec, 0x44, 0x0045 << 8, 0x0); + break; + case 0x10ec0236: + case 0x10ec0256: + alc_write_coef_idx(codec, 0x48, 0x0); + alc_update_coef_idx(codec, 0x49, 0x0045, 0x0); + break; + } +} + +static void alc_enable_headset_jack_key(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->has_hs_key) + return; + + switch (codec->core.vendor_id) { + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_write_coef_idx(codec, 0x48, 0xd011); + alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); + alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8); + break; + case 0x10ec0236: + case 0x10ec0256: + alc_write_coef_idx(codec, 0x48, 0xd011); + alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); + break; + } +} + +static void alc_fixup_headset_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->has_hs_key = 1; + snd_hda_jack_detect_enable_callback(codec, 0x55, + alc_headset_btn_callback); + snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", false, + SND_JACK_HEADSET, alc_headset_btn_keymap); + break; + case HDA_FIXUP_ACT_INIT: + alc_enable_headset_jack_key(codec); + break; + } +} + static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up) { alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0); @@ -3269,7 +3368,13 @@ static void alc256_init(struct hda_codec *codec) alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15); - alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/ + /* + * Expose headphone mic (or possibly Line In on some machines) instead + * of PC Beep on 1Ah, and disable 1Ah loopback for all outputs. See + * Documentation/sound/hd-audio/realtek-pc-beep.rst for details of + * this register. + */ + alc_write_coef_idx(codec, 0x36, 0x5757); } static void alc256_shutup(struct hda_codec *codec) @@ -3372,6 +3477,8 @@ static void alc225_shutup(struct hda_codec *codec) if (!hp_pin) hp_pin = 0x21; + + alc_disable_headset_jack_key(codec); /* 3k pull low control for Headset jack. */ alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); @@ -3411,6 +3518,9 @@ static void alc225_shutup(struct hda_codec *codec) alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4); msleep(30); } + + alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); + alc_enable_headset_jack_key(codec); } static void alc_default_init(struct hda_codec *codec) @@ -4008,6 +4118,12 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); } +static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_hp_gpio_led(codec, action, 0x04, 0x00); +} + static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -5375,17 +5491,6 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, } } -static void alc256_fixup_dell_xps_13_headphone_noise2(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 0, HDA_AMP_VOLMASK, 1); - snd_hda_override_wcaps(codec, 0x1a, get_wcaps(codec, 0x1a) & ~AC_WCAP_IN_AMP); -} - static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -5662,69 +5767,6 @@ static void alc285_fixup_invalidate_dacs(struct hda_codec *codec, snd_hda_override_wcaps(codec, 0x03, 0); } -static const struct hda_jack_keymap alc_headset_btn_keymap[] = { - { SND_JACK_BTN_0, KEY_PLAYPAUSE }, - { SND_JACK_BTN_1, KEY_VOICECOMMAND }, - { SND_JACK_BTN_2, KEY_VOLUMEUP }, - { SND_JACK_BTN_3, KEY_VOLUMEDOWN }, - {} -}; - -static void alc_headset_btn_callback(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - int report = 0; - - if (jack->unsol_res & (7 << 13)) - report |= SND_JACK_BTN_0; - - if (jack->unsol_res & (1 << 16 | 3 << 8)) - report |= SND_JACK_BTN_1; - - /* Volume up key */ - if (jack->unsol_res & (7 << 23)) - report |= SND_JACK_BTN_2; - - /* Volume down key */ - if (jack->unsol_res & (7 << 10)) - report |= SND_JACK_BTN_3; - - jack->jack->button_state = report; -} - -static void alc_fixup_headset_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_jack_detect_enable_callback(codec, 0x55, - alc_headset_btn_callback); - snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", false, - SND_JACK_HEADSET, alc_headset_btn_keymap); - break; - case HDA_FIXUP_ACT_INIT: - switch (codec->core.vendor_id) { - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_write_coef_idx(codec, 0x48, 0xd011); - alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); - alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8); - break; - case 0x10ec0236: - case 0x10ec0256: - alc_write_coef_idx(codec, 0x48, 0xd011); - alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); - break; - } - break; - } -} - static void alc295_fixup_chromebook(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -5863,8 +5905,6 @@ enum { ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, ALC275_FIXUP_DELL_XPS, - ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE, - ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2, ALC293_FIXUP_LENOVO_SPK_NOISE, ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, ALC255_FIXUP_DELL_SPK_NOISE, @@ -5923,6 +5963,7 @@ enum { ALC294_FIXUP_ASUS_DUAL_SPK, ALC285_FIXUP_THINKPAD_HEADSET_JACK, ALC294_FIXUP_ASUS_HPE, + ALC285_FIXUP_HP_GPIO_LED, }; static const struct hda_fixup alc269_fixups[] = { @@ -6604,23 +6645,6 @@ static const struct hda_fixup alc269_fixups[] = { {} } }, - [ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Disable pass-through path for FRONT 14h */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x36}, - {0x20, AC_VERB_SET_PROC_COEF, 0x1737}, - {} - }, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_fixup_dell_xps_13_headphone_noise2, - .chained = true, - .chain_id = ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE - }, [ALC293_FIXUP_LENOVO_SPK_NOISE] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_disable_aamix, @@ -7061,6 +7085,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC }, + [ALC285_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_gpio_led, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -7114,17 +7142,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2), SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE), SND_PCI_QUIRK(0x1028, 0x0738, "Dell Precision 5820", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2), SND_PCI_QUIRK(0x1028, 0x075c, "Dell XPS 27 7760", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3), SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), SND_PCI_QUIRK(0x1028, 0x080c, "Dell WYSE", ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x082a, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2), SND_PCI_QUIRK(0x1028, 0x084b, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), SND_PCI_QUIRK(0x1028, 0x084e, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), @@ -7208,6 +7233,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -7477,7 +7503,6 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc298-dell1"}, {.id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, .name = "alc298-dell-aio"}, {.id = ALC275_FIXUP_DELL_XPS, .name = "alc275-dell-xps"}, - {.id = ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE, .name = "alc256-dell-xps13"}, {.id = ALC293_FIXUP_LENOVO_SPK_NOISE, .name = "lenovo-spk-noise"}, {.id = ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, .name = "lenovo-hotkey"}, {.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"}, diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 21ab9cc50c71..65a887b217ee 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -30,7 +30,7 @@ #if K1212_DEBUG_LEVEL > 0 #define K1212_DEBUG_PRINTK(fmt,args...) printk(KERN_DEBUG fmt,##args) #else -#define K1212_DEBUG_PRINTK(fmt,...) +#define K1212_DEBUG_PRINTK(fmt,...) do { } while (0) #endif #if K1212_DEBUG_LEVEL > 1 #define K1212_DEBUG_PRINTK_VERBOSE(fmt,args...) printk(KERN_DEBUG fmt,##args) diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index cc06f0a1a7e4..227aece17e39 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -3353,7 +3353,8 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) return; } } else { - int err = -EINVAL; + int err; + err = hdsp_request_fw_loader(hdsp); if (err < 0) { snd_iprintf(buffer, diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 799789c8eea9..8b03e2dc503f 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -414,6 +414,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre { unsigned int i, idx, ofs, rest; struct via82xx *chip = snd_pcm_substream_chip(substream); + __le32 *pgtbl; if (dev->table.area == NULL) { /* the start of each lists must be aligned to 8 bytes, @@ -435,6 +436,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre /* fill the entries */ idx = 0; ofs = 0; + pgtbl = (__le32 *)dev->table.area; for (i = 0; i < periods; i++) { rest = fragsize; /* fill descriptors for a period. @@ -451,7 +453,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre return -EINVAL; } addr = snd_pcm_sgbuf_get_addr(substream, ofs); - ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr); + pgtbl[idx << 1] = cpu_to_le32(addr); r = snd_pcm_sgbuf_get_chunk_size(substream, ofs, rest); rest -= r; if (! rest) { @@ -466,7 +468,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre "tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest); */ - ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag); + pgtbl[(idx<<1) + 1] = cpu_to_le32(r | flag); dev->idx_table[idx].offset = ofs; dev->idx_table[idx].size = r; ofs += r; diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 84e589803e2e..607b7100db1c 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -267,6 +267,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre { unsigned int i, idx, ofs, rest; struct via82xx_modem *chip = snd_pcm_substream_chip(substream); + __le32 *pgtbl; if (dev->table.area == NULL) { /* the start of each lists must be aligned to 8 bytes, @@ -288,6 +289,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre /* fill the entries */ idx = 0; ofs = 0; + pgtbl = (__le32 *)dev->table.area; for (i = 0; i < periods; i++) { rest = fragsize; /* fill descriptors for a period. @@ -304,7 +306,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre return -EINVAL; } addr = snd_pcm_sgbuf_get_addr(substream, ofs); - ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr); + pgtbl[idx << 1] = cpu_to_le32(addr); r = PAGE_SIZE - (ofs % PAGE_SIZE); if (rest < r) r = rest; @@ -321,7 +323,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre "tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest); */ - ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag); + pgtbl[(idx<<1) + 1] = cpu_to_le32(r | flag); dev->idx_table[idx].offset = ofs; dev->idx_table[idx].size = r; ofs += r; diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index 093806d735c6..9554a0c506af 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -40,6 +40,7 @@ static int keywest_probe(struct i2c_client *client, static int keywest_attach_adapter(struct i2c_adapter *adapter) { struct i2c_board_info info; + struct i2c_client *client; if (! keywest_ctx) return -EINVAL; @@ -50,9 +51,11 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter) memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "keywest", I2C_NAME_SIZE); info.addr = keywest_ctx->addr; - keywest_ctx->client = i2c_new_device(adapter, &info); - if (!keywest_ctx->client) - return -ENODEV; + client = i2c_new_client_device(adapter, &info); + if (IS_ERR(client)) + return PTR_ERR(client); + keywest_ctx->client = client; + /* * We know the driver is already loaded, so the device should be * already bound. If not it means binding failed, and then there diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 5f40517717c4..bce4cee5cb54 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -26,3 +26,13 @@ config SND_SOC_AMD_ACP3x depends on X86 && PCI help This option enables ACP v3.x I2S support on AMD platform + +config SND_SOC_AMD_RV_RT5682_MACH + tristate "AMD RV support for RT5682" + select SND_SOC_RT5682 + select SND_SOC_MAX98357A + select SND_SOC_CROS_EC_CODEC + select I2C_CROS_EC_TUNNEL + depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC + help + This option enables machine driver for RT5682 and MAX9835. diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index c4ddc6adb6f0..e6f3d9b469f3 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -2,8 +2,10 @@ 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 obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/ +obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 7a5621e5e233..9414d7269c4f 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -54,7 +54,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) { int ret; struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = codec_dai->component; dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name); diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c index 91abeb92b648..73b31f88a6b5 100644 --- a/sound/soc/amd/acp-rt5645.c +++ b/sound/soc/amd/acp-rt5645.c @@ -48,7 +48,7 @@ static int cz_aif1_hw_params(struct snd_pcm_substream *substream, { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK, CZ_PLAT_CLK, params_rate(params) * 512); @@ -73,7 +73,7 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card; struct snd_soc_component *codec; - codec = rtd->codec_dai->component; + codec = asoc_rtd_to_codec(rtd, 0)->component; card = rtd->card; ret = snd_soc_card_jack_new(card, "Headset Jack", diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c new file mode 100644 index 000000000000..024a7ee54cd5 --- /dev/null +++ b/sound/soc/amd/acp3x-rt5682-max9836.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Machine driver for AMD ACP Audio engine using DA7219 & MAX98357 codec. +// +//Copyright 2016 Advanced Micro Devices, Inc. + +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-dapm.h> +#include <sound/jack.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/acpi.h> + +#include "raven/acp3x.h" +#include "../codecs/rt5682.h" + +#define PCO_PLAT_CLK 48000000 +#define RT5682_PLL_FREQ (48000 * 512) +#define DUAL_CHANNEL 2 + +static struct snd_soc_jack pco_jack; +static struct clk *rt5682_dai_wclk; +static struct clk *rt5682_dai_bclk; +static struct gpio_desc *dmic_sel; + +static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + + dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name); + + /* 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); + if (ret < 0) { + dev_err(rtd->card->dev, + "Failed to set rt5682 dai fmt: %d\n", ret); + return ret; + } + + /* set codec PLL */ + ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK, + PCO_PLAT_CLK, RT5682_PLL_FREQ); + if (ret < 0) { + dev_err(rtd->dev, "can't set rt5682 PLL: %d\n", ret); + return ret; + } + + /* Set codec sysclk */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2, + RT5682_PLL_FREQ, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, + "Failed to set rt5682 SYSCLK: %d\n", ret); + return ret; + } + + /* Set tdm/i2s1 master bclk ratio */ + ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64); + if (ret < 0) { + dev_err(rtd->dev, + "Failed to set rt5682 tdm bclk ratio: %d\n", ret); + return ret; + } + + rt5682_dai_wclk = clk_get(component->dev, "rt5682-dai-wclk"); + rt5682_dai_bclk = clk_get(component->dev, "rt5682-dai-bclk"); + + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pco_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "HP jack creation failed %d\n", ret); + return ret; + } + + snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ret = snd_soc_component_set_jack(component, &pco_jack, NULL); + if (ret) { + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + return ret; + } + + return ret; +} + +static int rt5682_clk_enable(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + /* RT5682 will support only 48K output with 48M mclk */ + clk_set_rate(rt5682_dai_wclk, 48000); + clk_set_rate(rt5682_dai_bclk, 48000 * 64); + ret = clk_prepare_enable(rt5682_dai_wclk); + if (ret < 0) { + dev_err(rtd->dev, "can't enable wclk %d\n", ret); + return ret; + } + + return ret; +} + +static void rt5682_clk_disable(void) +{ + clk_disable_unprepare(rt5682_dai_wclk); +} + +static const unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int acp3x_5682_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->play_i2s_instance = I2S_SP_INSTANCE; + machine->cap_i2s_instance = I2S_SP_INSTANCE; + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + return rt5682_clk_enable(substream); +} + +static int acp3x_max_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->play_i2s_instance = I2S_BT_INSTANCE; + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + return rt5682_clk_enable(substream); +} + +static int acp3x_ec_dmic0_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->cap_i2s_instance = I2S_BT_INSTANCE; + snd_soc_dai_set_bclk_ratio(codec_dai, 64); + if (dmic_sel) + gpiod_set_value(dmic_sel, 0); + + return rt5682_clk_enable(substream); +} + +static int acp3x_ec_dmic1_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->cap_i2s_instance = I2S_BT_INSTANCE; + snd_soc_dai_set_bclk_ratio(codec_dai, 64); + if (dmic_sel) + gpiod_set_value(dmic_sel, 1); + + return rt5682_clk_enable(substream); +} + +static void rt5682_shutdown(struct snd_pcm_substream *substream) +{ + rt5682_clk_disable(); +} + +static const struct snd_soc_ops acp3x_5682_ops = { + .startup = acp3x_5682_startup, + .shutdown = rt5682_shutdown, +}; + +static const struct snd_soc_ops acp3x_max_play_ops = { + .startup = acp3x_max_startup, + .shutdown = rt5682_shutdown, +}; + +static const struct snd_soc_ops acp3x_ec_cap0_ops = { + .startup = acp3x_ec_dmic0_startup, + .shutdown = rt5682_shutdown, +}; + +static const struct snd_soc_ops acp3x_ec_cap1_ops = { + .startup = acp3x_ec_dmic1_startup, + .shutdown = rt5682_shutdown, +}; + +SND_SOC_DAILINK_DEF(acp3x_i2s, + DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.0"))); +SND_SOC_DAILINK_DEF(acp3x_bt, + DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.2"))); + +SND_SOC_DAILINK_DEF(rt5682, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1"))); +SND_SOC_DAILINK_DEF(max, + DAILINK_COMP_ARRAY(COMP_CODEC("MX98357A:00", "HiFi"))); +SND_SOC_DAILINK_DEF(cros_ec, + DAILINK_COMP_ARRAY(COMP_CODEC("GOOG0013:00", "EC Codec I2S RX"))); + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("acp3x_rv_i2s_dma.0"))); + +static struct snd_soc_dai_link acp3x_dai_5682_98357[] = { + { + .name = "acp3x-5682-play", + .stream_name = "Playback", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .init = acp3x_5682_init, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &acp3x_5682_ops, + SND_SOC_DAILINK_REG(acp3x_i2s, rt5682, platform), + }, + { + .name = "acp3x-max98357-play", + .stream_name = "HiFi Playback", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .dpcm_playback = 1, + .ops = &acp3x_max_play_ops, + SND_SOC_DAILINK_REG(acp3x_bt, max, platform), + }, + { + .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, + .dpcm_capture = 1, + .ops = &acp3x_ec_cap0_ops, + SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform), + }, + { + .name = "acp3x-ec-dmic1-capture", + .stream_name = "Capture DMIC1", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_capture = 1, + .ops = &acp3x_ec_cap1_ops, + SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform), + }, +}; + +static const struct snd_soc_dapm_widget acp3x_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route acp3x_audio_route[] = { + {"Headphone Jack", NULL, "HPOL"}, + {"Headphone Jack", NULL, "HPOR"}, + {"IN1P", NULL, "Headset Mic"}, + {"Spk", NULL, "Speaker"}, +}; + +static const struct snd_kcontrol_new acp3x_mc_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Spk"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static struct snd_soc_card acp3x_card = { + .name = "acp3xalc5682m98357", + .owner = THIS_MODULE, + .dai_link = acp3x_dai_5682_98357, + .num_links = ARRAY_SIZE(acp3x_dai_5682_98357), + .dapm_widgets = acp3x_widgets, + .num_dapm_widgets = ARRAY_SIZE(acp3x_widgets), + .dapm_routes = acp3x_audio_route, + .num_dapm_routes = ARRAY_SIZE(acp3x_audio_route), + .controls = acp3x_mc_controls, + .num_controls = ARRAY_SIZE(acp3x_mc_controls), +}; + +static int acp3x_probe(struct platform_device *pdev) +{ + int ret; + struct snd_soc_card *card; + struct acp3x_platform_info *machine; + + machine = devm_kzalloc(&pdev->dev, sizeof(*machine), GFP_KERNEL); + if (!machine) + return -ENOMEM; + + card = &acp3x_card; + acp3x_card.dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW); + if (IS_ERR(dmic_sel)) { + dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", + PTR_ERR(dmic_sel)); + return PTR_ERR(dmic_sel); + } + + ret = devm_snd_soc_register_card(&pdev->dev, &acp3x_card); + if (ret) { + dev_err(&pdev->dev, + "devm_snd_soc_register_card(%s) failed: %d\n", + acp3x_card.name, ret); + return ret; + } + return 0; +} + +static const struct acpi_device_id acp3x_audio_acpi_match[] = { + { "AMDI5682", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, acp3x_audio_acpi_match); + +static struct platform_driver acp3x_audio = { + .driver = { + .name = "acp3x-alc5682-max98357", + .acpi_match_table = ACPI_PTR(acp3x_audio_acpi_match), + .pm = &snd_soc_pm_ops, + }, + .probe = acp3x_probe, +}; + +module_platform_driver(acp3x_audio); + +MODULE_AUTHOR("akshu.agrawal@amd.com"); +MODULE_DESCRIPTION("ALC5682 & MAX98357 audio support"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c index 91a388184e52..3a3c47e820ab 100644 --- a/sound/soc/amd/raven/acp3x-i2s.c +++ b/sound/soc/amd/raven/acp3x-i2s.c @@ -42,7 +42,7 @@ static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, u32 rx_mask, int slots, int slot_width) { struct i2s_dev_data *adata; - u32 val, reg_val, frmt_reg, frm_len; + u32 frm_len; u16 slot_len; adata = snd_soc_dai_get_drvdata(cpu_dai); @@ -64,36 +64,7 @@ static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, default: return -EINVAL; } - - /* Enable I2S/BT channels TDM, respective TX/RX frame lengths.*/ - frm_len = FRM_LEN | (slots << 15) | (slot_len << 18); - if (adata->substream_type == SNDRV_PCM_STREAM_PLAYBACK) { - switch (adata->i2s_instance) { - case I2S_BT_INSTANCE: - reg_val = mmACP_BTTDM_ITER; - frmt_reg = mmACP_BTTDM_TXFRMT; - break; - case I2S_SP_INSTANCE: - default: - reg_val = mmACP_I2STDM_ITER; - frmt_reg = mmACP_I2STDM_TXFRMT; - } - } else { - switch (adata->i2s_instance) { - case I2S_BT_INSTANCE: - reg_val = mmACP_BTTDM_IRER; - frmt_reg = mmACP_BTTDM_RXFRMT; - break; - case I2S_SP_INSTANCE: - default: - reg_val = mmACP_I2STDM_IRER; - frmt_reg = mmACP_I2STDM_RXFRMT; - } - } - val = rv_readl(adata->acp3x_base + reg_val); - rv_writel(val | 0x2, adata->acp3x_base + reg_val); - rv_writel(frm_len, adata->acp3x_base + frmt_reg); adata->tdm_fmt = frm_len; return 0; } @@ -105,12 +76,14 @@ static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *prtd; struct snd_soc_card *card; struct acp3x_platform_info *pinfo; + struct i2s_dev_data *adata; u32 val; - u32 reg_val; + u32 reg_val, frmt_reg; prtd = substream->private_data; rtd = substream->runtime->private_data; card = prtd->card; + adata = snd_soc_dai_get_drvdata(dai); pinfo = snd_soc_card_get_drvdata(card); if (pinfo) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -141,21 +114,30 @@ static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream, switch (rtd->i2s_instance) { case I2S_BT_INSTANCE: reg_val = mmACP_BTTDM_ITER; + frmt_reg = mmACP_BTTDM_TXFRMT; break; case I2S_SP_INSTANCE: default: reg_val = mmACP_I2STDM_ITER; + frmt_reg = mmACP_I2STDM_TXFRMT; } } else { switch (rtd->i2s_instance) { case I2S_BT_INSTANCE: reg_val = mmACP_BTTDM_IRER; + frmt_reg = mmACP_BTTDM_RXFRMT; break; case I2S_SP_INSTANCE: default: reg_val = mmACP_I2STDM_IRER; + frmt_reg = mmACP_I2STDM_RXFRMT; } } + if (adata->tdm_mode) { + val = rv_readl(rtd->acp3x_base + reg_val); + rv_writel(val | 0x2, rtd->acp3x_base + reg_val); + rv_writel(adata->tdm_fmt, rtd->acp3x_base + frmt_reg); + } val = rv_readl(rtd->acp3x_base + reg_val); val = val | (rtd->xfer_resolution << 3); rv_writel(val, rtd->acp3x_base + reg_val); diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index d62c0d90c41e..e362f0bc9e46 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -458,7 +458,8 @@ static int acp3x_resume(struct device *dev) reg_val = mmACP_I2STDM_ITER; frmt_val = mmACP_I2STDM_TXFRMT; } - rv_writel((rtd->xfer_resolution << 3), rtd->acp3x_base + reg_val); + rv_writel((rtd->xfer_resolution << 3), + rtd->acp3x_base + reg_val); } if (adata->capture_stream && adata->capture_stream->runtime) { struct i2s_stream_instance *rtd = @@ -474,7 +475,8 @@ static int acp3x_resume(struct device *dev) reg_val = mmACP_I2STDM_IRER; frmt_val = mmACP_I2STDM_RXFRMT; } - rv_writel((rtd->xfer_resolution << 3), rtd->acp3x_base + reg_val); + rv_writel((rtd->xfer_resolution << 3), + rtd->acp3x_base + reg_val); } if (adata->tdm_mode == TDM_ENABLE) { rv_writel(adata->tdm_fmt, adata->acp3x_base + frmt_val); diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c index da60e2ec5535..f25ce50f1a90 100644 --- a/sound/soc/amd/raven/pci-acp3x.c +++ b/sound/soc/amd/raven/pci-acp3x.c @@ -38,8 +38,13 @@ static int acp3x_power_on(void __iomem *acp3x_base) timeout = 0; while (++timeout < 500) { val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); - if (!val) + if (!val) { + /* Set PME_EN as after ACP power On, + * PME_EN gets cleared + */ + rv_writel(0x1, acp3x_base + mmACP_PME_EN); return 0; + } udelay(1); } return -ETIMEDOUT; diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index db67f5ba1e9a..cb03c4f7324c 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -56,7 +56,7 @@ static void atmel_pcm_dma_irq(u32 ssc_sr, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct atmel_pcm_dma_params *prtd; - prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + prtd = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); if (ssc_sr & prtd->mask->ssc_error) { if (snd_pcm_running(substream)) @@ -83,7 +83,7 @@ static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream, struct ssc_device *ssc; int ret; - prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + prtd = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); ssc = prtd->ssc; ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index 59c1331a6984..a8daebcbf6c8 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -213,7 +213,7 @@ static int atmel_pcm_hw_params(struct snd_soc_component *component, snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = params_buffer_bytes(params); - prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + prtd->params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); prtd->params->dma_intr_handler = atmel_pcm_dma_irq; prtd->dma_buffer = runtime->dma_addr; diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index 776b27d3686e..148c943cb538 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -27,7 +27,7 @@ static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK, diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c index befc2a3a05b0..3cb63886195f 100644 --- a/sound/soc/atmel/mchp-i2s-mcc.c +++ b/sound/soc/atmel/mchp-i2s-mcc.c @@ -239,10 +239,10 @@ struct mchp_i2s_mcc_dev { unsigned int frame_length; int tdm_slots; int channels; - int gclk_use:1; - int gclk_running:1; - int tx_rdy:1; - int rx_rdy:1; + unsigned int gclk_use:1; + unsigned int gclk_running:1; + unsigned int tx_rdy:1; + unsigned int rx_rdy:1; }; static irqreturn_t mchp_i2s_mcc_interrupt(int irq, void *dev_id) diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c index aa6d0d78566f..f9a85fd01b79 100644 --- a/sound/soc/atmel/mikroe-proto.c +++ b/sound/soc/atmel/mikroe-proto.c @@ -21,7 +21,7 @@ static int snd_proto_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); /* Set proto sysclk */ int ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index b1bef2bf142d..ed1f69b57024 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -96,7 +96,7 @@ static const struct snd_soc_dapm_route intercon[] = { */ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct device *dev = rtd->dev; int ret; diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c index 7822425d5e61..9fbc3c1113cc 100644 --- a/sound/soc/atmel/sam9x5_wm8731.c +++ b/sound/soc/atmel/sam9x5_wm8731.c @@ -40,7 +40,7 @@ struct sam9x5_drvdata { */ static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct device *dev = rtd->dev; int ret; diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index d6b692fff29a..d649037bda9b 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -95,7 +95,7 @@ static struct snd_soc_card db1550_ac97_machine = { static int db1200_i2s_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); /* WM8731 has its own 12MHz crystal */ snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 8f855644c6b4..e82bbf2d1eea 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -281,7 +281,7 @@ static int au1xpsc_pcm_open(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = substream->private_data; int stype = substream->stream, *dmaids; - dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dmaids = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); if (!dmaids) return -ENODEV; /* whoa, has ordering changed? */ diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c index c9a038a5e2d3..4e246c7e78f2 100644 --- a/sound/soc/au1x/dma.c +++ b/sound/soc/au1x/dma.c @@ -195,7 +195,7 @@ static int alchemy_pcm_open(struct snd_soc_component *component, int *dmaids, s = substream->stream; char *name; - dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dmaids = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); if (!dmaids) return -ENODEV; /* whoa, has ordering changed? */ diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 0227993c5da8..05eb36991f14 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -58,7 +58,7 @@ static struct au1xpsc_audio_data *au1xpsc_ac97_workdata; static inline struct au1xpsc_audio_data *ac97_to_pscdata(struct snd_ac97 *x) { struct snd_soc_card *c = x->bus->card->private_data; - return snd_soc_dai_get_drvdata(c->rtd->cpu_dai); + return snd_soc_dai_get_drvdata(c->asoc_rtd_to_cpu(rtd, 0)); } #else diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig index 0037e96aa228..4218057b0874 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -17,3 +17,12 @@ config SND_SOC_CYGNUS Cygnus chips (bcm958300, bcm958305, bcm911360) If you don't know what to do here, say N. + +config SND_BCM63XX_I2S_WHISTLER + tristate "SoC Audio support for the Broadcom BCM63XX I2S module" + select REGMAP_MMIO + help + Say Y if you want to add support for ASoC audio on Broadcom + DSL/PON chips (bcm63158, bcm63178) + + If you don't know what to do here, say N diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile index b81fa421ec27..7c2d7899603b 100644 --- a/sound/soc/bcm/Makefile +++ b/sound/soc/bcm/Makefile @@ -9,3 +9,7 @@ snd-soc-cygnus-objs := cygnus-pcm.o cygnus-ssp.o obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o +# BCM63XX Platform Support +snd-soc-63xx-objs := bcm63xx-i2s-whistler.o bcm63xx-pcm-whistler.o + +obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o
\ No newline at end of file diff --git a/sound/soc/bcm/bcm63xx-i2s-whistler.c b/sound/soc/bcm/bcm63xx-i2s-whistler.c new file mode 100644 index 000000000000..246a57ac6679 --- /dev/null +++ b/sound/soc/bcm/bcm63xx-i2s-whistler.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// linux/sound/bcm/bcm63xx-i2s-whistler.c +// BCM63xx whistler i2s driver +// Copyright (c) 2020 Broadcom Corporation +// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com> + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "bcm63xx-i2s.h" + +#define DRV_NAME "brcm-i2s" + +static bool brcm_i2s_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case I2S_TX_CFG ... I2S_TX_DESC_IFF_LEN: + case I2S_TX_CFG_2 ... I2S_RX_DESC_IFF_LEN: + case I2S_RX_CFG_2 ... I2S_REG_MAX: + return true; + default: + return false; + } +} + +static bool brcm_i2s_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case I2S_TX_CFG ... I2S_REG_MAX: + return true; + default: + return false; + } +} + +static bool brcm_i2s_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case I2S_TX_CFG: + case I2S_TX_IRQ_CTL: + case I2S_TX_DESC_IFF_ADDR: + case I2S_TX_DESC_IFF_LEN: + case I2S_TX_DESC_OFF_ADDR: + case I2S_TX_DESC_OFF_LEN: + case I2S_TX_CFG_2: + case I2S_RX_CFG: + case I2S_RX_IRQ_CTL: + case I2S_RX_DESC_OFF_ADDR: + case I2S_RX_DESC_OFF_LEN: + case I2S_RX_DESC_IFF_LEN: + case I2S_RX_DESC_IFF_ADDR: + case I2S_RX_CFG_2: + return true; + default: + return false; + } +} + +static const struct regmap_config brcm_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = I2S_REG_MAX, + .writeable_reg = brcm_i2s_wr_reg, + .readable_reg = brcm_i2s_rd_reg, + .volatile_reg = brcm_i2s_volatile_reg, + .cache_type = REGCACHE_FLAT, +}; + +static int bcm63xx_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai); + + ret = clk_set_rate(i2s_priv->i2s_clk, params_rate(params)); + if (ret < 0) + dev_err(i2s_priv->dev, + "Can't set sample rate, err: %d\n", ret); + + return ret; +} + +static int bcm63xx_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + unsigned int slavemode; + struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai); + struct regmap *regmap_i2s = i2s_priv->regmap_i2s; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(regmap_i2s, I2S_TX_CFG, + I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT | + I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE, + I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT | + I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE); + regmap_write(regmap_i2s, I2S_TX_IRQ_CTL, 0); + regmap_write(regmap_i2s, I2S_TX_IRQ_IFF_THLD, 0); + regmap_write(regmap_i2s, I2S_TX_IRQ_OFF_THLD, 1); + + /* TX and RX block each have an independent bit to indicate + * if it is generating the clock for the I2S bus. The bus + * clocks need to be generated from either the TX or RX block, + * but not both + */ + regmap_read(regmap_i2s, I2S_RX_CFG_2, &slavemode); + if (slavemode & I2S_RX_SLAVE_MODE_MASK) + regmap_update_bits(regmap_i2s, I2S_TX_CFG_2, + I2S_TX_SLAVE_MODE_MASK, + I2S_TX_MASTER_MODE); + else + regmap_update_bits(regmap_i2s, I2S_TX_CFG_2, + I2S_TX_SLAVE_MODE_MASK, + I2S_TX_SLAVE_MODE); + } else { + regmap_update_bits(regmap_i2s, I2S_RX_CFG, + I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT | + I2S_RX_CLOCK_ENABLE, + I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT | + I2S_RX_CLOCK_ENABLE); + regmap_write(regmap_i2s, I2S_RX_IRQ_CTL, 0); + regmap_write(regmap_i2s, I2S_RX_IRQ_IFF_THLD, 0); + regmap_write(regmap_i2s, I2S_RX_IRQ_OFF_THLD, 1); + + regmap_read(regmap_i2s, I2S_TX_CFG_2, &slavemode); + if (slavemode & I2S_TX_SLAVE_MODE_MASK) + regmap_update_bits(regmap_i2s, I2S_RX_CFG_2, + I2S_RX_SLAVE_MODE_MASK, 0); + else + regmap_update_bits(regmap_i2s, I2S_RX_CFG_2, + I2S_RX_SLAVE_MODE_MASK, + I2S_RX_SLAVE_MODE); + } + return 0; +} + +static void bcm63xx_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + unsigned int enabled, slavemode; + struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai); + struct regmap *regmap_i2s = i2s_priv->regmap_i2s; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(regmap_i2s, I2S_TX_CFG, + I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT | + I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE, 0); + regmap_write(regmap_i2s, I2S_TX_IRQ_CTL, 1); + regmap_write(regmap_i2s, I2S_TX_IRQ_IFF_THLD, 4); + regmap_write(regmap_i2s, I2S_TX_IRQ_OFF_THLD, 4); + + regmap_read(regmap_i2s, I2S_TX_CFG_2, &slavemode); + slavemode = slavemode & I2S_TX_SLAVE_MODE_MASK; + if (!slavemode) { + regmap_read(regmap_i2s, I2S_RX_CFG, &enabled); + enabled = enabled & I2S_RX_ENABLE_MASK; + if (enabled) + regmap_update_bits(regmap_i2s, I2S_RX_CFG_2, + I2S_RX_SLAVE_MODE_MASK, + I2S_RX_MASTER_MODE); + } + regmap_update_bits(regmap_i2s, I2S_TX_CFG_2, + I2S_TX_SLAVE_MODE_MASK, + I2S_TX_SLAVE_MODE); + } else { + regmap_update_bits(regmap_i2s, I2S_RX_CFG, + I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT | + I2S_RX_CLOCK_ENABLE, 0); + regmap_write(regmap_i2s, I2S_RX_IRQ_CTL, 1); + regmap_write(regmap_i2s, I2S_RX_IRQ_IFF_THLD, 4); + regmap_write(regmap_i2s, I2S_RX_IRQ_OFF_THLD, 4); + + regmap_read(regmap_i2s, I2S_RX_CFG_2, &slavemode); + slavemode = slavemode & I2S_RX_SLAVE_MODE_MASK; + if (!slavemode) { + regmap_read(regmap_i2s, I2S_TX_CFG, &enabled); + enabled = enabled & I2S_TX_ENABLE_MASK; + if (enabled) + regmap_update_bits(regmap_i2s, I2S_TX_CFG_2, + I2S_TX_SLAVE_MODE_MASK, + I2S_TX_MASTER_MODE); + } + + regmap_update_bits(regmap_i2s, I2S_RX_CFG_2, + I2S_RX_SLAVE_MODE_MASK, I2S_RX_SLAVE_MODE); + } +} + +static const struct snd_soc_dai_ops bcm63xx_i2s_dai_ops = { + .startup = bcm63xx_i2s_startup, + .shutdown = bcm63xx_i2s_shutdown, + .hw_params = bcm63xx_i2s_hw_params, +}; + +static struct snd_soc_dai_driver bcm63xx_i2s_dai = { + .name = DRV_NAME, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &bcm63xx_i2s_dai_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, +}; + +static const struct snd_soc_component_driver bcm63xx_i2s_component = { + .name = "bcm63xx", +}; + +static int bcm63xx_i2s_dev_probe(struct platform_device *pdev) +{ + int ret = 0; + void __iomem *regs; + struct resource *r_mem, *region; + struct bcm_i2s_priv *i2s_priv; + struct regmap *regmap_i2s; + struct clk *i2s_clk; + + i2s_priv = devm_kzalloc(&pdev->dev, sizeof(*i2s_priv), GFP_KERNEL); + if (!i2s_priv) + return -ENOMEM; + + i2s_clk = devm_clk_get(&pdev->dev, "i2sclk"); + if (IS_ERR(i2s_clk)) { + dev_err(&pdev->dev, "%s: cannot get a brcm clock: %ld\n", + __func__, PTR_ERR(i2s_clk)); + return PTR_ERR(i2s_clk); + } + + r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r_mem) { + dev_err(&pdev->dev, "Unable to get register resource.\n"); + return -ENODEV; + } + + region = devm_request_mem_region(&pdev->dev, r_mem->start, + resource_size(r_mem), DRV_NAME); + if (!region) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + return -EBUSY; + } + + regs = devm_ioremap_resource(&pdev->dev, r_mem); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + return ret; + } + + regmap_i2s = devm_regmap_init_mmio(&pdev->dev, + regs, &brcm_i2s_regmap_config); + if (IS_ERR(regmap_i2s)) + return PTR_ERR(regmap_i2s); + + regmap_update_bits(regmap_i2s, I2S_MISC_CFG, + I2S_PAD_LVL_LOOP_DIS_MASK, + I2S_PAD_LVL_LOOP_DIS_ENABLE); + + ret = devm_snd_soc_register_component(&pdev->dev, + &bcm63xx_i2s_component, + &bcm63xx_i2s_dai, 1); + if (ret) { + dev_err(&pdev->dev, "failed to register the dai\n"); + return ret; + } + + i2s_priv->dev = &pdev->dev; + i2s_priv->i2s_clk = i2s_clk; + i2s_priv->regmap_i2s = regmap_i2s; + dev_set_drvdata(&pdev->dev, i2s_priv); + + ret = bcm63xx_soc_platform_probe(pdev, i2s_priv); + if (ret) + dev_err(&pdev->dev, "failed to register the pcm\n"); + + return ret; +} + +static int bcm63xx_i2s_dev_remove(struct platform_device *pdev) +{ + bcm63xx_soc_platform_remove(pdev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id snd_soc_bcm_audio_match[] = { + {.compatible = "brcm,bcm63xx-i2s"}, + { } +}; +#endif + +static struct platform_driver bcm63xx_i2s_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(snd_soc_bcm_audio_match), + }, + .probe = bcm63xx_i2s_dev_probe, + .remove = bcm63xx_i2s_dev_remove, +}; + +module_platform_driver(bcm63xx_i2s_driver); + +MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom DSL XPON ASOC I2S Interface"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/bcm/bcm63xx-i2s.h b/sound/soc/bcm/bcm63xx-i2s.h new file mode 100644 index 000000000000..edc328ba53d3 --- /dev/null +++ b/sound/soc/bcm/bcm63xx-i2s.h @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// linux/sound/soc/bcm/bcm63xx-i2s.h +// Copyright (c) 2020 Broadcom Corporation +// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com> + +#ifndef __BCM63XX_I2S_H +#define __BCM63XX_I2S_H + +#define I2S_DESC_FIFO_DEPTH 8 +#define I2S_MISC_CFG (0x003C) +#define I2S_PAD_LVL_LOOP_DIS_MASK (1 << 2) +#define I2S_PAD_LVL_LOOP_DIS_ENABLE I2S_PAD_LVL_LOOP_DIS_MASK + +#define I2S_TX_ENABLE_MASK (1 << 31) +#define I2S_TX_ENABLE I2S_TX_ENABLE_MASK +#define I2S_TX_OUT_R (1 << 19) +#define I2S_TX_DATA_ALIGNMENT (1 << 2) +#define I2S_TX_DATA_ENABLE (1 << 1) +#define I2S_TX_CLOCK_ENABLE (1 << 0) + +#define I2S_TX_DESC_OFF_LEVEL_SHIFT 12 +#define I2S_TX_DESC_OFF_LEVEL_MASK (0x0F << I2S_TX_DESC_OFF_LEVEL_SHIFT) +#define I2S_TX_DESC_IFF_LEVEL_SHIFT 8 +#define I2S_TX_DESC_IFF_LEVEL_MASK (0x0F << I2S_TX_DESC_IFF_LEVEL_SHIFT) +#define I2S_TX_DESC_OFF_INTR_EN_MSK (1 << 1) +#define I2S_TX_DESC_OFF_INTR_EN I2S_TX_DESC_OFF_INTR_EN_MSK + +#define I2S_TX_CFG (0x0000) +#define I2S_TX_IRQ_CTL (0x0004) +#define I2S_TX_IRQ_EN (0x0008) +#define I2S_TX_IRQ_IFF_THLD (0x000c) +#define I2S_TX_IRQ_OFF_THLD (0x0010) +#define I2S_TX_DESC_IFF_ADDR (0x0014) +#define I2S_TX_DESC_IFF_LEN (0x0018) +#define I2S_TX_DESC_OFF_ADDR (0x001C) +#define I2S_TX_DESC_OFF_LEN (0x0020) +#define I2S_TX_CFG_2 (0x0024) +#define I2S_TX_SLAVE_MODE_SHIFT 13 +#define I2S_TX_SLAVE_MODE_MASK (1 << I2S_TX_SLAVE_MODE_SHIFT) +#define I2S_TX_SLAVE_MODE I2S_TX_SLAVE_MODE_MASK +#define I2S_TX_MASTER_MODE 0 +#define I2S_TX_INTR_MASK 0x0F + +#define I2S_RX_ENABLE_MASK (1 << 31) +#define I2S_RX_ENABLE I2S_RX_ENABLE_MASK +#define I2S_RX_IN_R (1 << 19) +#define I2S_RX_DATA_ALIGNMENT (1 << 2) +#define I2S_RX_CLOCK_ENABLE (1 << 0) + +#define I2S_RX_DESC_OFF_LEVEL_SHIFT 12 +#define I2S_RX_DESC_OFF_LEVEL_MASK (0x0F << I2S_RX_DESC_OFF_LEVEL_SHIFT) +#define I2S_RX_DESC_IFF_LEVEL_SHIFT 8 +#define I2S_RX_DESC_IFF_LEVEL_MASK (0x0F << I2S_RX_DESC_IFF_LEVEL_SHIFT) +#define I2S_RX_DESC_OFF_INTR_EN_MSK (1 << 1) +#define I2S_RX_DESC_OFF_INTR_EN I2S_RX_DESC_OFF_INTR_EN_MSK + +#define I2S_RX_CFG (0x0040) /* 20c0 */ +#define I2S_RX_IRQ_CTL (0x0044) +#define I2S_RX_IRQ_EN (0x0048) +#define I2S_RX_IRQ_IFF_THLD (0x004C) +#define I2S_RX_IRQ_OFF_THLD (0x0050) +#define I2S_RX_DESC_IFF_ADDR (0x0054) +#define I2S_RX_DESC_IFF_LEN (0x0058) +#define I2S_RX_DESC_OFF_ADDR (0x005C) +#define I2S_RX_DESC_OFF_LEN (0x0060) +#define I2S_RX_CFG_2 (0x0064) +#define I2S_RX_SLAVE_MODE_SHIFT 13 +#define I2S_RX_SLAVE_MODE_MASK (1 << I2S_RX_SLAVE_MODE_SHIFT) +#define I2S_RX_SLAVE_MODE I2S_RX_SLAVE_MODE_MASK +#define I2S_RX_MASTER_MODE 0 +#define I2S_RX_INTR_MASK 0x0F + +#define I2S_REG_MAX 0x007C + +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; + struct snd_pcm_substream *capture_substream; + struct i2s_dma_desc *play_dma_desc; + struct i2s_dma_desc *capture_dma_desc; +}; + +extern int bcm63xx_soc_platform_probe(struct platform_device *pdev, + struct bcm_i2s_priv *i2s_priv); +extern int bcm63xx_soc_platform_remove(struct platform_device *pdev); + +#endif diff --git a/sound/soc/bcm/bcm63xx-pcm-whistler.c b/sound/soc/bcm/bcm63xx-pcm-whistler.c new file mode 100644 index 000000000000..e46c390683e7 --- /dev/null +++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// linux/sound/bcm/bcm63xx-pcm-whistler.c +// BCM63xx whistler pcm interface +// Copyright (c) 2020 Broadcom Corporation +// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com> + +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/module.h> +#include <sound/pcm_params.h> +#include <linux/regmap.h> +#include <linux/of_device.h> +#include <sound/soc.h> +#include "bcm63xx-i2s.h" + + +struct i2s_dma_desc { + unsigned char *dma_area; + dma_addr_t dma_addr; + unsigned int dma_len; +}; + +struct bcm63xx_runtime_data { + int dma_len; + dma_addr_t dma_addr; + dma_addr_t dma_addr_next; +}; + +static const struct snd_pcm_hardware bcm63xx_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */ + .period_bytes_max = 8192 - 32, + .periods_min = 1, + .periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc), + .buffer_bytes_max = 128 * 1024, + .fifo_size = 32, +}; + +static int bcm63xx_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct i2s_dma_desc *dma_desc; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = params_buffer_bytes(params); + + dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT); + if (!dma_desc) + return -ENOMEM; + + snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_desc); + + return 0; +} + +static int bcm63xx_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct i2s_dma_desc *dma_desc; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); + kfree(dma_desc); + snd_pcm_set_runtime_buffer(substream, NULL); + + return 0; +} + +static int bcm63xx_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd; + struct bcm_i2s_priv *i2s_priv; + struct regmap *regmap_i2s; + + rtd = substream->private_data; + i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev); + regmap_i2s = i2s_priv->regmap_i2s; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + regmap_update_bits(regmap_i2s, + I2S_TX_IRQ_EN, + I2S_TX_DESC_OFF_INTR_EN, + I2S_TX_DESC_OFF_INTR_EN); + regmap_update_bits(regmap_i2s, + I2S_TX_CFG, + I2S_TX_ENABLE_MASK, + I2S_TX_ENABLE); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + regmap_write(regmap_i2s, + I2S_TX_IRQ_EN, + 0); + regmap_update_bits(regmap_i2s, + I2S_TX_CFG, + I2S_TX_ENABLE_MASK, + 0); + break; + default: + ret = -EINVAL; + } + } else { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + regmap_update_bits(regmap_i2s, + I2S_RX_IRQ_EN, + I2S_RX_DESC_OFF_INTR_EN_MSK, + I2S_RX_DESC_OFF_INTR_EN); + regmap_update_bits(regmap_i2s, + I2S_RX_CFG, + I2S_RX_ENABLE_MASK, + I2S_RX_ENABLE); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + regmap_update_bits(regmap_i2s, + I2S_RX_IRQ_EN, + I2S_RX_DESC_OFF_INTR_EN_MSK, + 0); + regmap_update_bits(regmap_i2s, + I2S_RX_CFG, + I2S_RX_ENABLE_MASK, + 0); + break; + default: + ret = -EINVAL; + } + } + return ret; +} + +static int bcm63xx_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct i2s_dma_desc *dma_desc; + struct regmap *regmap_i2s; + struct bcm_i2s_priv *i2s_priv; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + uint32_t regaddr_desclen, regaddr_descaddr; + + dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); + dma_desc->dma_len = snd_pcm_lib_period_bytes(substream); + dma_desc->dma_addr = runtime->dma_addr; + dma_desc->dma_area = runtime->dma_area; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regaddr_desclen = I2S_TX_DESC_IFF_LEN; + regaddr_descaddr = I2S_TX_DESC_IFF_ADDR; + } else { + regaddr_desclen = I2S_RX_DESC_IFF_LEN; + regaddr_descaddr = I2S_RX_DESC_IFF_ADDR; + } + + i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev); + regmap_i2s = i2s_priv->regmap_i2s; + + regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len); + regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr); + + return 0; +} + +static snd_pcm_uframes_t +bcm63xx_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + snd_pcm_uframes_t x; + struct bcm63xx_runtime_data *prtd = substream->runtime->private_data; + + if ((void *)prtd->dma_addr_next == NULL) + prtd->dma_addr_next = substream->runtime->dma_addr; + + x = bytes_to_frames(substream->runtime, + prtd->dma_addr_next - substream->runtime->dma_addr); + + return x == substream->runtime->buffer_size ? 0 : x; +} + +static int bcm63xx_pcm_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_wc(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); + +} + +static int bcm63xx_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct bcm63xx_runtime_data *prtd; + + runtime->hw = bcm63xx_pcm_hardware; + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret) + goto out; + + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret) + goto out; + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + goto out; + + ret = -ENOMEM; + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (!prtd) + goto out; + + runtime->private_data = prtd; + return 0; +out: + return ret; +} + +static int bcm63xx_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct bcm63xx_runtime_data *prtd = runtime->private_data; + + kfree(prtd); + return 0; +} + +static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv) +{ + unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2; + struct bcm63xx_runtime_data *prtd; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + struct regmap *regmap_i2s; + struct i2s_dma_desc *dma_desc; + struct snd_soc_pcm_runtime *rtd; + struct bcm_i2s_priv *i2s_priv; + + i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv; + regmap_i2s = i2s_priv->regmap_i2s; + + /* rx */ + regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status); + + if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) { + substream = i2s_priv->capture_substream; + runtime = substream->runtime; + rtd = substream->private_data; + prtd = runtime->private_data; + dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); + + offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >> + I2S_RX_DESC_OFF_LEVEL_SHIFT; + while (offlevel) { + regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1); + regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2); + offlevel--; + } + prtd->dma_addr_next = val_1 + val_2; + ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >> + I2S_RX_DESC_IFF_LEVEL_SHIFT; + + availdepth = I2S_DESC_FIFO_DEPTH - ifflevel; + while (availdepth) { + dma_desc->dma_addr += + snd_pcm_lib_period_bytes(substream); + dma_desc->dma_area += + snd_pcm_lib_period_bytes(substream); + if (dma_desc->dma_addr - runtime->dma_addr >= + runtime->dma_bytes) { + dma_desc->dma_addr = runtime->dma_addr; + dma_desc->dma_area = runtime->dma_area; + } + + prtd->dma_addr = dma_desc->dma_addr; + regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN, + snd_pcm_lib_period_bytes(substream)); + regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR, + dma_desc->dma_addr); + availdepth--; + } + + snd_pcm_period_elapsed(substream); + + /* Clear interrupt by writing 0 */ + regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL, + I2S_RX_INTR_MASK, 0); + } + + /* tx */ + regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status); + + if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) { + substream = i2s_priv->play_substream; + runtime = substream->runtime; + rtd = substream->private_data; + prtd = runtime->private_data; + dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); + + offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >> + I2S_TX_DESC_OFF_LEVEL_SHIFT; + while (offlevel) { + regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1); + regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN, &val_2); + prtd->dma_addr_next = val_1 + val_2; + offlevel--; + } + + ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >> + I2S_TX_DESC_IFF_LEVEL_SHIFT; + availdepth = I2S_DESC_FIFO_DEPTH - ifflevel; + + while (availdepth) { + dma_desc->dma_addr += + snd_pcm_lib_period_bytes(substream); + dma_desc->dma_area += + snd_pcm_lib_period_bytes(substream); + + if (dma_desc->dma_addr - runtime->dma_addr >= + runtime->dma_bytes) { + dma_desc->dma_addr = runtime->dma_addr; + dma_desc->dma_area = runtime->dma_area; + } + + prtd->dma_addr = dma_desc->dma_addr; + regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN, + snd_pcm_lib_period_bytes(substream)); + regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR, + dma_desc->dma_addr); + availdepth--; + } + + snd_pcm_period_elapsed(substream); + + /* Clear interrupt by writing 0 */ + regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL, + I2S_TX_INTR_MASK, 0); + } + + return IRQ_HANDLED; +} + +static int bcm63xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = bcm63xx_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + + buf->area = dma_alloc_wc(pcm->card->dev, + size, &buf->addr, + GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + buf->bytes = size; + return 0; +} + +static int bcm63xx_soc_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct bcm_i2s_priv *i2s_priv; + int ret; + + i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev); + + of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1); + + ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32)); + if (ret) + goto out; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ret = bcm63xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + + i2s_priv->play_substream = + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ret = bcm63xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + i2s_priv->capture_substream = + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + } + +out: + return ret; +} + +static void bcm63xx_pcm_free_dma_buffers(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + int stream; + struct snd_dma_buffer *buf; + struct snd_pcm_substream *substream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + buf = &substream->dma_buffer; + if (!buf->area) + continue; + dma_free_wc(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static const struct snd_soc_component_driver bcm63xx_soc_platform = { + .open = bcm63xx_pcm_open, + .close = bcm63xx_pcm_close, + .hw_params = bcm63xx_pcm_hw_params, + .hw_free = bcm63xx_pcm_hw_free, + .prepare = bcm63xx_pcm_prepare, + .trigger = bcm63xx_pcm_trigger, + .pointer = bcm63xx_pcm_pointer, + .mmap = bcm63xx_pcm_mmap, + .pcm_construct = bcm63xx_soc_pcm_new, + .pcm_destruct = bcm63xx_pcm_free_dma_buffers, +}; + +int bcm63xx_soc_platform_probe(struct platform_device *pdev, + struct bcm_i2s_priv *i2s_priv) +{ + 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 = devm_request_irq(&pdev->dev, i2s_priv->r_irq->start, i2s_dma_isr, + i2s_priv->r_irq->flags, "i2s_dma", (void *)i2s_priv); + if (ret) { + dev_err(&pdev->dev, + "i2s_init: failed to request interrupt.ret=%d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(&pdev->dev, + &bcm63xx_soc_platform, NULL, 0); +} + +int bcm63xx_soc_platform_remove(struct platform_device *pdev) +{ + return 0; +} + +MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c index 3a80c613bc3f..f96d27c8b301 100644 --- a/sound/soc/bcm/cygnus-pcm.c +++ b/sound/soc/bcm/cygnus-pcm.c @@ -209,7 +209,7 @@ static struct cygnus_aio_port *cygnus_dai_get_dma_data( { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - return snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); + return snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(soc_runtime, 0), substream); } static void ringbuf_set_initial(void __iomem *audio_io, @@ -359,7 +359,7 @@ static void disable_intr(struct snd_pcm_substream *substream) aio = cygnus_dai_get_dma_data(substream); - dev_dbg(rtd->cpu_dai->dev, "%s on port %d\n", __func__, aio->portnum); + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum); /* The port number maps to the bit position to be set */ set_mask = BIT(aio->portnum); @@ -590,7 +590,7 @@ static int cygnus_pcm_open(struct snd_soc_component *component, if (!aio) return -ENODEV; - dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum); + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw); @@ -623,7 +623,7 @@ static int cygnus_pcm_close(struct snd_soc_component *component, aio = cygnus_dai_get_dma_data(substream); - dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum); + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) aio->play_stream = NULL; @@ -631,7 +631,7 @@ static int cygnus_pcm_close(struct snd_soc_component *component, aio->capture_stream = NULL; if (!aio->play_stream && !aio->capture_stream) - dev_dbg(rtd->cpu_dai->dev, "freed port %d\n", aio->portnum); + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "freed port %d\n", aio->portnum); return 0; } @@ -645,7 +645,7 @@ static int cygnus_pcm_hw_params(struct snd_soc_component *component, struct cygnus_aio_port *aio; aio = cygnus_dai_get_dma_data(substream); - dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum); + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = params_buffer_bytes(params); @@ -660,7 +660,7 @@ static int cygnus_pcm_hw_free(struct snd_soc_component *component, struct cygnus_aio_port *aio; aio = cygnus_dai_get_dma_data(substream); - dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum); + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); snd_pcm_set_runtime_buffer(substream, NULL); return 0; @@ -678,12 +678,12 @@ static int cygnus_pcm_prepare(struct snd_soc_component *component, struct ringbuf_regs *p_rbuf = NULL; aio = cygnus_dai_get_dma_data(substream); - dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum); + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); bufsize = snd_pcm_lib_buffer_bytes(substream); periodsize = snd_pcm_lib_period_bytes(substream); - dev_dbg(rtd->cpu_dai->dev, "%s (buf_size %lu) (period_size %lu)\n", + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n", __func__, bufsize, periodsize); configure_ringbuf_regs(substream); @@ -745,11 +745,11 @@ static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) buf->area = dma_alloc_coherent(pcm->card->dev, size, &buf->addr, GFP_KERNEL); - dev_dbg(rtd->cpu_dai->dev, "%s: size 0x%zx @ %pK\n", + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s: size 0x%zx @ %pK\n", __func__, size, buf->area); if (!buf->area) { - dev_err(rtd->cpu_dai->dev, "%s: dma_alloc failed\n", __func__); + dev_err(asoc_rtd_to_cpu(rtd, 0)->dev, "%s: dma_alloc failed\n", __func__); return -ENOMEM; } buf->bytes = size; diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c index 10961190068e..ccf65f087ea6 100644 --- a/sound/soc/cirrus/edb93xx.c +++ b/sound/soc/cirrus/edb93xx.c @@ -23,8 +23,8 @@ static int edb93xx_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int err; unsigned int mclk_rate; unsigned int rate = params_rate(params); diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c index 70c2f3e08d6d..cb133e80b7c3 100644 --- a/sound/soc/cirrus/snappercl15.c +++ b/sound/soc/cirrus/snappercl15.c @@ -23,8 +23,8 @@ static int snappercl15_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int err; err = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ea912439e446..e6a0c5d05fa5 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -14,262 +14,264 @@ menu "CODEC drivers" config SND_SOC_ALL_CODECS tristate "Build all ASoC CODEC drivers" depends on COMPILE_TEST - select SND_SOC_88PM860X if MFD_88PM860X - select SND_SOC_L3 - select SND_SOC_AB8500_CODEC if ABX500_CORE - select SND_SOC_AC97_CODEC - select SND_SOC_AD1836 if SPI_MASTER - select SND_SOC_AD193X_SPI if SPI_MASTER - select SND_SOC_AD193X_I2C if I2C - select SND_SOC_AD1980 if SND_SOC_AC97_BUS - select SND_SOC_AD73311 - select SND_SOC_ADAU1373 if I2C - select SND_SOC_ADAU1761_I2C if I2C - select SND_SOC_ADAU1761_SPI if SPI - select SND_SOC_ADAU1781_I2C if I2C - select SND_SOC_ADAU1781_SPI if SPI - select SND_SOC_ADAV801 if SPI_MASTER - select SND_SOC_ADAV803 if I2C - select SND_SOC_ADAU1977_SPI if SPI_MASTER - select SND_SOC_ADAU1977_I2C if I2C - select SND_SOC_ADAU1701 if I2C - select SND_SOC_ADAU7002 - select SND_SOC_ADAU7118_I2C if I2C - select SND_SOC_ADAU7118_HW - select SND_SOC_ADS117X - select SND_SOC_AK4104 if SPI_MASTER - select SND_SOC_AK4118 if I2C - select SND_SOC_AK4458 if I2C - select SND_SOC_AK4535 if I2C - select SND_SOC_AK4554 - select SND_SOC_AK4613 if I2C - select SND_SOC_AK4641 if I2C - select SND_SOC_AK4642 if I2C - select SND_SOC_AK4671 if I2C - select SND_SOC_AK5386 - select SND_SOC_AK5558 if I2C - select SND_SOC_ALC5623 if I2C - select SND_SOC_ALC5632 if I2C - select SND_SOC_BT_SCO - select SND_SOC_BD28623 - select SND_SOC_CQ0093VC - select SND_SOC_CROS_EC_CODEC if CROS_EC - select SND_SOC_CS35L32 if I2C - select SND_SOC_CS35L33 if I2C - select SND_SOC_CS35L34 if I2C - select SND_SOC_CS35L35 if I2C - select SND_SOC_CS35L36 if I2C - select SND_SOC_CS42L42 if I2C - select SND_SOC_CS42L51_I2C if I2C - select SND_SOC_CS42L52 if I2C && INPUT - select SND_SOC_CS42L56 if I2C && INPUT - select SND_SOC_CS42L73 if I2C - select SND_SOC_CS4265 if I2C - select SND_SOC_CS4270 if I2C - select SND_SOC_CS4271_I2C if I2C - select SND_SOC_CS4271_SPI if SPI_MASTER - select SND_SOC_CS42XX8_I2C if I2C - select SND_SOC_CS43130 if I2C - select SND_SOC_CS4341 if SND_SOC_I2C_AND_SPI - select SND_SOC_CS4349 if I2C - select SND_SOC_CS47L15 if MFD_CS47L15 - select SND_SOC_CS47L24 if MFD_CS47L24 - select SND_SOC_CS47L35 if MFD_CS47L35 - select SND_SOC_CS47L85 if MFD_CS47L85 - select SND_SOC_CS47L90 if MFD_CS47L90 - select SND_SOC_CS47L92 if MFD_CS47L92 - select SND_SOC_CS53L30 if I2C - select SND_SOC_CX20442 if TTY - select SND_SOC_CX2072X if I2C - select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI - select SND_SOC_DA7213 if I2C - select SND_SOC_DA7218 if I2C - select SND_SOC_DA7219 if I2C - select SND_SOC_DA732X if I2C - select SND_SOC_DA9055 if I2C - select SND_SOC_DMIC if GPIOLIB - select SND_SOC_ES8316 if I2C - select SND_SOC_ES8328_SPI if SPI_MASTER - select SND_SOC_ES8328_I2C if I2C - select SND_SOC_ES7134 - select SND_SOC_ES7241 - select SND_SOC_GTM601 - select SND_SOC_HDAC_HDMI - select SND_SOC_HDAC_HDA - select SND_SOC_ICS43432 - select SND_SOC_INNO_RK3036 - select SND_SOC_ISABELLE if I2C - select SND_SOC_JZ4740_CODEC - select SND_SOC_JZ4725B_CODEC - select SND_SOC_JZ4770_CODEC - select SND_SOC_LM4857 if I2C - select SND_SOC_LM49453 if I2C - select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR - select SND_SOC_MAX98088 if I2C - select SND_SOC_MAX98090 if I2C - select SND_SOC_MAX98095 if I2C - select SND_SOC_MAX98357A if GPIOLIB - select SND_SOC_MAX98371 if I2C - select SND_SOC_MAX98504 if I2C - select SND_SOC_MAX9867 if I2C - select SND_SOC_MAX98925 if I2C - select SND_SOC_MAX98926 if I2C - select SND_SOC_MAX98927 if I2C - select SND_SOC_MAX98373 if I2C - select SND_SOC_MAX9850 if I2C - select SND_SOC_MAX9860 if I2C - select SND_SOC_MAX9759 - select SND_SOC_MAX9768 if I2C - select SND_SOC_MAX9877 if I2C - select SND_SOC_MC13783 if MFD_MC13XXX - select SND_SOC_ML26124 if I2C - select SND_SOC_MT6351 if MTK_PMIC_WRAP - select SND_SOC_MT6358 if MTK_PMIC_WRAP - select SND_SOC_MT6660 if I2C - select SND_SOC_NAU8540 if I2C - select SND_SOC_NAU8810 if I2C - select SND_SOC_NAU8822 if I2C - select SND_SOC_NAU8824 if I2C - select SND_SOC_NAU8825 if I2C - select SND_SOC_HDMI_CODEC - select SND_SOC_PCM1681 if I2C - select SND_SOC_PCM1789_I2C if I2C - select SND_SOC_PCM179X_I2C if I2C - select SND_SOC_PCM179X_SPI if SPI_MASTER - select SND_SOC_PCM186X_I2C if I2C - select SND_SOC_PCM186X_SPI if SPI_MASTER - select SND_SOC_PCM3008 - select SND_SOC_PCM3060_I2C if I2C - select SND_SOC_PCM3060_SPI if SPI_MASTER - select SND_SOC_PCM3168A_I2C if I2C - select SND_SOC_PCM3168A_SPI if SPI_MASTER - select SND_SOC_PCM5102A - select SND_SOC_PCM512x_I2C if I2C - select SND_SOC_PCM512x_SPI if SPI_MASTER - select SND_SOC_RK3328 - select SND_SOC_RT274 if I2C - select SND_SOC_RT286 if I2C - select SND_SOC_RT298 if I2C - select SND_SOC_RT1011 if I2C - select SND_SOC_RT1015 if I2C - select SND_SOC_RT1305 if I2C - select SND_SOC_RT1308 if I2C - select SND_SOC_RT5514 if I2C - select SND_SOC_RT5616 if I2C - select SND_SOC_RT5631 if I2C - select SND_SOC_RT5640 if I2C - select SND_SOC_RT5645 if I2C - select SND_SOC_RT5651 if I2C - select SND_SOC_RT5659 if I2C - select SND_SOC_RT5660 if I2C - select SND_SOC_RT5663 if I2C - select SND_SOC_RT5665 if I2C - select SND_SOC_RT5668 if I2C - select SND_SOC_RT5670 if I2C - select SND_SOC_RT5677 if I2C && SPI_MASTER - select SND_SOC_RT5682 if I2C - select SND_SOC_RT700_SDW if SOUNDWIRE - select SND_SOC_RT711_SDW if SOUNDWIRE - select SND_SOC_RT715_SDW if SOUNDWIRE - select SND_SOC_RT1308_SDW if SOUNDWIRE - select SND_SOC_SGTL5000 if I2C - select SND_SOC_SI476X if MFD_SI476X_CORE - select SND_SOC_SIMPLE_AMPLIFIER - select SND_SOC_SIRF_AUDIO_CODEC - select SND_SOC_SPDIF - select SND_SOC_SSM2305 - select SND_SOC_SSM2518 if I2C - select SND_SOC_SSM2602_SPI if SPI_MASTER - select SND_SOC_SSM2602_I2C if I2C - select SND_SOC_SSM4567 if I2C - select SND_SOC_STA32X if I2C - select SND_SOC_STA350 if I2C - select SND_SOC_STA529 if I2C - select SND_SOC_STAC9766 if SND_SOC_AC97_BUS - select SND_SOC_STI_SAS - select SND_SOC_TAS2552 if I2C - select SND_SOC_TAS2562 if I2C - select SND_SOC_TAS2770 if I2C - select SND_SOC_TAS5086 if I2C - select SND_SOC_TAS571X if I2C - select SND_SOC_TAS5720 if I2C - select SND_SOC_TAS6424 if I2C - select SND_SOC_TDA7419 if I2C - select SND_SOC_TFA9879 if I2C - select SND_SOC_TLV320AIC23_I2C if I2C - select SND_SOC_TLV320AIC23_SPI if SPI_MASTER - select SND_SOC_TLV320AIC26 if SPI_MASTER - select SND_SOC_TLV320AIC31XX if I2C - select SND_SOC_TLV320AIC32X4_I2C if I2C && COMMON_CLK - select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER && COMMON_CLK - select SND_SOC_TLV320AIC3X if I2C - select SND_SOC_TPA6130A2 if I2C - select SND_SOC_TLV320DAC33 if I2C - select SND_SOC_TSCS42XX if I2C - select SND_SOC_TSCS454 if I2C - select SND_SOC_TS3A227E if I2C - select SND_SOC_TWL4030 if TWL4030_CORE - select SND_SOC_TWL6040 if TWL6040_CORE - select SND_SOC_UDA1334 if GPIOLIB - select SND_SOC_UDA134X - select SND_SOC_UDA1380 if I2C - select SND_SOC_WCD9335 if SLIMBUS - select SND_SOC_WCD934X if MFD_WCD934X && COMMON_CLK - select SND_SOC_WL1273 if MFD_WL1273_CORE - select SND_SOC_WM0010 if SPI_MASTER - select SND_SOC_WM1250_EV1 if I2C - select SND_SOC_WM2000 if I2C - select SND_SOC_WM2200 if I2C - select SND_SOC_WM5100 if I2C - select SND_SOC_WM5102 if MFD_WM5102 - select SND_SOC_WM5110 if MFD_WM5110 - select SND_SOC_WM8350 if MFD_WM8350 - select SND_SOC_WM8400 if MFD_WM8400 - select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8523 if I2C - select SND_SOC_WM8524 if GPIOLIB - select SND_SOC_WM8580 if I2C - select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8727 - select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8737 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8741 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8770 if SPI_MASTER - select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8782 - select SND_SOC_WM8804_I2C if I2C - select SND_SOC_WM8804_SPI if SPI_MASTER - select SND_SOC_WM8900 if I2C - select SND_SOC_WM8903 if I2C - select SND_SOC_WM8904 if I2C - select SND_SOC_WM8940 if I2C - select SND_SOC_WM8955 if I2C - select SND_SOC_WM8960 if I2C - select SND_SOC_WM8961 if I2C - select SND_SOC_WM8962 if I2C && INPUT - select SND_SOC_WM8971 if I2C - select SND_SOC_WM8974 if I2C - select SND_SOC_WM8978 if I2C - select SND_SOC_WM8983 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8990 if I2C - select SND_SOC_WM8991 if I2C - select SND_SOC_WM8993 if I2C - select SND_SOC_WM8994 if MFD_WM8994 - select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI - select SND_SOC_WM8996 if I2C - select SND_SOC_WM8997 if MFD_WM8997 - select SND_SOC_WM8998 if MFD_WM8998 - select SND_SOC_WM9081 if I2C - select SND_SOC_WM9090 if I2C - select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) - select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) - select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) - select SND_SOC_WSA881X if SOUNDWIRE + imply SND_SOC_88PM860X + imply SND_SOC_L3 + imply SND_SOC_AB8500_CODEC + imply SND_SOC_AC97_CODEC + imply SND_SOC_AD1836 + imply SND_SOC_AD193X_SPI + imply SND_SOC_AD193X_I2C + imply SND_SOC_AD1980 + imply SND_SOC_AD73311 + imply SND_SOC_ADAU1373 + imply SND_SOC_ADAU1761_I2C + imply SND_SOC_ADAU1761_SPI + imply SND_SOC_ADAU1781_I2C + imply SND_SOC_ADAU1781_SPI + imply SND_SOC_ADAV801 + imply SND_SOC_ADAV803 + imply SND_SOC_ADAU1977_SPI + imply SND_SOC_ADAU1977_I2C + imply SND_SOC_ADAU1701 + imply SND_SOC_ADAU7002 + imply SND_SOC_ADAU7118_I2C + imply SND_SOC_ADAU7118_HW + imply SND_SOC_ADS117X + imply SND_SOC_AK4104 + imply SND_SOC_AK4118 + imply SND_SOC_AK4458 + imply SND_SOC_AK4535 + imply SND_SOC_AK4554 + imply SND_SOC_AK4613 + imply SND_SOC_AK4641 + imply SND_SOC_AK4642 + imply SND_SOC_AK4671 + imply SND_SOC_AK5386 + imply SND_SOC_AK5558 + imply SND_SOC_ALC5623 + imply SND_SOC_ALC5632 + imply SND_SOC_BT_SCO + imply SND_SOC_BD28623 + imply SND_SOC_CQ0093VC + imply SND_SOC_CROS_EC_CODEC + imply SND_SOC_CS35L32 + imply SND_SOC_CS35L33 + imply SND_SOC_CS35L34 + imply SND_SOC_CS35L35 + imply SND_SOC_CS35L36 + imply SND_SOC_CS42L42 + imply SND_SOC_CS42L51_I2C + imply SND_SOC_CS42L52 + imply SND_SOC_CS42L56 + imply SND_SOC_CS42L73 + imply SND_SOC_CS4265 + imply SND_SOC_CS4270 + imply SND_SOC_CS4271_I2C + imply SND_SOC_CS4271_SPI + imply SND_SOC_CS42XX8_I2C + imply SND_SOC_CS43130 + imply SND_SOC_CS4341 + imply SND_SOC_CS4349 + imply SND_SOC_CS47L15 + imply SND_SOC_CS47L24 + imply SND_SOC_CS47L35 + imply SND_SOC_CS47L85 + imply SND_SOC_CS47L90 + imply SND_SOC_CS47L92 + imply SND_SOC_CS53L30 + imply SND_SOC_CX20442 + imply SND_SOC_CX2072X + imply SND_SOC_DA7210 + imply SND_SOC_DA7213 + imply SND_SOC_DA7218 + imply SND_SOC_DA7219 + imply SND_SOC_DA732X + imply SND_SOC_DA9055 + imply SND_SOC_DMIC + imply SND_SOC_ES8316 + imply SND_SOC_ES8328_SPI + imply SND_SOC_ES8328_I2C + imply SND_SOC_ES7134 + imply SND_SOC_ES7241 + imply SND_SOC_GTM601 + imply SND_SOC_HDAC_HDMI + imply SND_SOC_HDAC_HDA + imply SND_SOC_ICS43432 + imply SND_SOC_INNO_RK3036 + imply SND_SOC_ISABELLE + imply SND_SOC_JZ4740_CODEC + imply SND_SOC_JZ4725B_CODEC + imply SND_SOC_JZ4770_CODEC + imply SND_SOC_LM4857 + imply SND_SOC_LM49453 + imply SND_SOC_LOCHNAGAR_SC + imply SND_SOC_MAX98088 + imply SND_SOC_MAX98090 + imply SND_SOC_MAX98095 + imply SND_SOC_MAX98357A + imply SND_SOC_MAX98371 + imply SND_SOC_MAX98504 + imply SND_SOC_MAX9867 + imply SND_SOC_MAX98925 + imply SND_SOC_MAX98926 + imply SND_SOC_MAX98927 + imply SND_SOC_MAX98373 + imply SND_SOC_MAX9850 + imply SND_SOC_MAX9860 + imply SND_SOC_MAX9759 + imply SND_SOC_MAX9768 + imply SND_SOC_MAX9877 + imply SND_SOC_MC13783 + imply SND_SOC_ML26124 + imply SND_SOC_MT6351 + imply SND_SOC_MT6358 + imply SND_SOC_MT6660 + imply SND_SOC_NAU8540 + imply SND_SOC_NAU8810 + imply SND_SOC_NAU8822 + imply SND_SOC_NAU8824 + imply SND_SOC_NAU8825 + imply SND_SOC_HDMI_CODEC + imply SND_SOC_PCM1681 + imply SND_SOC_PCM1789_I2C + imply SND_SOC_PCM179X_I2C + imply SND_SOC_PCM179X_SPI + imply SND_SOC_PCM186X_I2C + imply SND_SOC_PCM186X_SPI + imply SND_SOC_PCM3008 + imply SND_SOC_PCM3060_I2C + imply SND_SOC_PCM3060_SPI + imply SND_SOC_PCM3168A_I2C + imply SND_SOC_PCM3168A_SPI + imply SND_SOC_PCM5102A + imply SND_SOC_PCM512x_I2C + imply SND_SOC_PCM512x_SPI + imply SND_SOC_RK3328 + imply SND_SOC_RT274 + imply SND_SOC_RT286 + imply SND_SOC_RT298 + imply SND_SOC_RT1011 + imply SND_SOC_RT1015 + imply SND_SOC_RT1305 + imply SND_SOC_RT1308 + imply SND_SOC_RT5514 + imply SND_SOC_RT5616 + imply SND_SOC_RT5631 + imply SND_SOC_RT5640 + imply SND_SOC_RT5645 + imply SND_SOC_RT5651 + imply SND_SOC_RT5659 + imply SND_SOC_RT5660 + imply SND_SOC_RT5663 + imply SND_SOC_RT5665 + imply SND_SOC_RT5668 + imply SND_SOC_RT5670 + imply SND_SOC_RT5677 + imply SND_SOC_RT5682 + imply SND_SOC_RT5682_SDW + imply SND_SOC_RT700_SDW + imply SND_SOC_RT711_SDW + imply SND_SOC_RT715_SDW + imply SND_SOC_RT1308_SDW + imply SND_SOC_SGTL5000 + imply SND_SOC_SI476X + imply SND_SOC_SIMPLE_AMPLIFIER + imply SND_SOC_SIRF_AUDIO_CODEC + imply SND_SOC_SPDIF + imply SND_SOC_SSM2305 + imply SND_SOC_SSM2518 + imply SND_SOC_SSM2602_SPI + imply SND_SOC_SSM2602_I2C + imply SND_SOC_SSM4567 + imply SND_SOC_STA32X + imply SND_SOC_STA350 + imply SND_SOC_STA529 + imply SND_SOC_STAC9766 + imply SND_SOC_STI_SAS + imply SND_SOC_TAS2552 + imply SND_SOC_TAS2562 + imply SND_SOC_TAS2770 + imply SND_SOC_TAS5086 + imply SND_SOC_TAS571X + imply SND_SOC_TAS5720 + imply SND_SOC_TAS6424 + imply SND_SOC_TDA7419 + imply SND_SOC_TFA9879 + imply SND_SOC_TLV320ADCX140 + imply SND_SOC_TLV320AIC23_I2C + imply SND_SOC_TLV320AIC23_SPI + imply SND_SOC_TLV320AIC26 + imply SND_SOC_TLV320AIC31XX + imply SND_SOC_TLV320AIC32X4_I2C + imply SND_SOC_TLV320AIC32X4_SPI + imply SND_SOC_TLV320AIC3X + imply SND_SOC_TPA6130A2 + imply SND_SOC_TLV320DAC33 + imply SND_SOC_TSCS42XX + imply SND_SOC_TSCS454 + imply SND_SOC_TS3A227E + imply SND_SOC_TWL4030 + imply SND_SOC_TWL6040 + imply SND_SOC_UDA1334 + imply SND_SOC_UDA134X + imply SND_SOC_UDA1380 + imply SND_SOC_WCD9335 + imply SND_SOC_WCD934X + imply SND_SOC_WL1273 + imply SND_SOC_WM0010 + imply SND_SOC_WM1250_EV1 + imply SND_SOC_WM2000 + imply SND_SOC_WM2200 + imply SND_SOC_WM5100 + imply SND_SOC_WM5102 + imply SND_SOC_WM5110 + imply SND_SOC_WM8350 + imply SND_SOC_WM8400 + imply SND_SOC_WM8510 + imply SND_SOC_WM8523 + imply SND_SOC_WM8524 + imply SND_SOC_WM8580 + imply SND_SOC_WM8711 + imply SND_SOC_WM8727 + imply SND_SOC_WM8728 + imply SND_SOC_WM8731 + imply SND_SOC_WM8737 + imply SND_SOC_WM8741 + imply SND_SOC_WM8750 + imply SND_SOC_WM8753 + imply SND_SOC_WM8770 + imply SND_SOC_WM8776 + imply SND_SOC_WM8782 + imply SND_SOC_WM8804_I2C + imply SND_SOC_WM8804_SPI + imply SND_SOC_WM8900 + imply SND_SOC_WM8903 + imply SND_SOC_WM8904 + imply SND_SOC_WM8940 + imply SND_SOC_WM8955 + imply SND_SOC_WM8960 + imply SND_SOC_WM8961 + imply SND_SOC_WM8962 + imply SND_SOC_WM8971 + imply SND_SOC_WM8974 + imply SND_SOC_WM8978 + imply SND_SOC_WM8983 + imply SND_SOC_WM8985 + imply SND_SOC_WM8988 + imply SND_SOC_WM8990 + imply SND_SOC_WM8991 + imply SND_SOC_WM8993 + imply SND_SOC_WM8994 + imply SND_SOC_WM8995 + imply SND_SOC_WM8996 + imply SND_SOC_WM8997 + imply SND_SOC_WM8998 + imply SND_SOC_WM9081 + imply SND_SOC_WM9090 + imply SND_SOC_WM9705 + imply SND_SOC_WM9712 + imply SND_SOC_WM9713 + imply SND_SOC_WSA881X help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -283,6 +285,7 @@ config SND_SOC_ALL_CODECS config SND_SOC_88PM860X tristate + depends on MFD_88PM860X config SND_SOC_ARIZONA tristate @@ -318,6 +321,7 @@ config SND_SOC_WM_ADSP config SND_SOC_AB8500_CODEC tristate + depends on ABX500_CORE config SND_SOC_AC97_CODEC tristate "Build generic ASoC AC97 CODEC driver" @@ -326,21 +330,25 @@ config SND_SOC_AC97_CODEC config SND_SOC_AD1836 tristate + depends on SPI_MASTER config SND_SOC_AD193X tristate config SND_SOC_AD193X_SPI tristate + depends on SPI_MASTER select SND_SOC_AD193X config SND_SOC_AD193X_I2C tristate + depends on I2C select SND_SOC_AD193X config SND_SOC_AD1980 - select REGMAP_AC97 tristate + depends on SND_SOC_AC97_BUS + select REGMAP_AC97 config SND_SOC_AD73311 tristate @@ -350,6 +358,7 @@ config SND_SOC_ADAU_UTILS config SND_SOC_ADAU1373 tristate + depends on I2C select SND_SOC_ADAU_UTILS config SND_SOC_ADAU1701 @@ -384,11 +393,13 @@ config SND_SOC_ADAU1781 config SND_SOC_ADAU1781_I2C tristate + depends on I2C select SND_SOC_ADAU1781 select REGMAP_I2C config SND_SOC_ADAU1781_SPI tristate + depends on SPI_MASTER select SND_SOC_ADAU1781 select REGMAP_SPI @@ -397,11 +408,13 @@ config SND_SOC_ADAU1977 config SND_SOC_ADAU1977_SPI tristate + depends on SPI_MASTER select SND_SOC_ADAU1977 select REGMAP_SPI config SND_SOC_ADAU1977_I2C tristate + depends on I2C select SND_SOC_ADAU1977 select REGMAP_I2C @@ -440,10 +453,12 @@ config SND_SOC_ADAV80X config SND_SOC_ADAV801 tristate + depends on SPI_MASTER select SND_SOC_ADAV80X config SND_SOC_ADAV803 tristate + depends on I2C select SND_SOC_ADAV80X config SND_SOC_ADS117X @@ -465,6 +480,7 @@ config SND_SOC_AK4458 config SND_SOC_AK4535 tristate + depends on I2C config SND_SOC_AK4554 tristate "AKM AK4554 CODEC" @@ -475,6 +491,7 @@ config SND_SOC_AK4613 config SND_SOC_AK4641 tristate + depends on I2C config SND_SOC_AK4642 tristate "AKM AK4642 CODEC" @@ -482,6 +499,7 @@ config SND_SOC_AK4642 config SND_SOC_AK4671 tristate + depends on I2C config SND_SOC_AK5386 tristate "AKM AK5638 CODEC" @@ -497,6 +515,7 @@ config SND_SOC_ALC5623 config SND_SOC_ALC5632 tristate + depends on I2C config SND_SOC_BD28623 tristate "ROHM BD28623 CODEC" @@ -631,6 +650,7 @@ config SND_SOC_CS47L15 config SND_SOC_CS47L24 tristate + depends on MFD_CS47L24 config SND_SOC_CS47L35 tristate @@ -697,6 +717,7 @@ config SND_SOC_L3 config SND_SOC_DA7210 tristate + depends on I2C config SND_SOC_DA7213 tristate "Dialog DA7213 CODEC" @@ -704,15 +725,19 @@ config SND_SOC_DA7213 config SND_SOC_DA7218 tristate + depends on I2C config SND_SOC_DA7219 tristate + depends on I2C config SND_SOC_DA732X tristate + depends on I2C config SND_SOC_DA9055 tristate + depends on I2C config SND_SOC_DMIC tristate "Generic Digital Microphone CODEC" @@ -772,9 +797,11 @@ config SND_SOC_INNO_RK3036 config SND_SOC_ISABELLE tristate + depends on I2C config SND_SOC_LM49453 tristate + depends on I2C config SND_SOC_LOCHNAGAR_SC tristate "Lochnagar Sound Card" @@ -801,17 +828,20 @@ config SND_SOC_MAX98088 depends on I2C config SND_SOC_MAX98090 - tristate + tristate + depends on I2C config SND_SOC_MAX98095 - tristate + tristate + depends on I2C config SND_SOC_MAX98357A tristate "Maxim MAX98357A CODEC" depends on GPIOLIB config SND_SOC_MAX98371 - tristate + tristate + depends on I2C config SND_SOC_MAX98504 tristate "Maxim MAX98504 speaker amplifier" @@ -822,10 +852,12 @@ config SND_SOC_MAX9867 depends on I2C config SND_SOC_MAX98925 - tristate + tristate + depends on I2C config SND_SOC_MAX98926 tristate + depends on I2C config SND_SOC_MAX98927 tristate "Maxim Integrated MAX98927 Speaker Amplifier" @@ -837,6 +869,7 @@ config SND_SOC_MAX98373 config SND_SOC_MAX9850 tristate + depends on I2C config SND_SOC_MAX9860 tristate "Maxim MAX9860 Mono Audio Voice Codec" @@ -1015,26 +1048,32 @@ config SND_SOC_RT298 config SND_SOC_RT1011 tristate + depends on I2C config SND_SOC_RT1015 tristate + depends on I2C config SND_SOC_RT1305 tristate + depends on I2C config SND_SOC_RT1308 tristate + depends on I2C config SND_SOC_RT1308_SDW tristate "Realtek RT1308 Codec - SDW" - depends on SOUNDWIRE + depends on I2C && SOUNDWIRE select REGMAP_SOUNDWIRE config SND_SOC_RT5514 tristate + depends on I2C config SND_SOC_RT5514_SPI tristate + depends on SPI_MASTER config SND_SOC_RT5514_SPI_BUILTIN bool # force RT5514_SPI to be built-in to avoid link errors @@ -1050,33 +1089,43 @@ config SND_SOC_RT5631 config SND_SOC_RT5640 tristate + depends on I2C config SND_SOC_RT5645 tristate + depends on I2C config SND_SOC_RT5651 tristate + depends on I2C config SND_SOC_RT5659 tristate + depends on I2C config SND_SOC_RT5660 tristate + depends on I2C config SND_SOC_RT5663 tristate + depends on I2C config SND_SOC_RT5665 tristate + depends on I2C config SND_SOC_RT5668 tristate + depends on I2C config SND_SOC_RT5670 tristate + depends on I2C config SND_SOC_RT5677 tristate + depends on I2C select REGMAP_I2C select REGMAP_IRQ @@ -1086,6 +1135,13 @@ config SND_SOC_RT5677_SPI config SND_SOC_RT5682 tristate + depends on I2C || SOUNDWIRE + +config SND_SOC_RT5682_SDW + tristate "Realtek RT5682 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT5682 + select REGMAP_SOUNDWIRE config SND_SOC_RT700 tristate @@ -1153,6 +1209,7 @@ config SND_SOC_SSM2305 config SND_SOC_SSM2518 tristate + depends on I2C config SND_SOC_SSM2602 tristate @@ -1184,9 +1241,11 @@ config SND_SOC_STA350 config SND_SOC_STA529 tristate + depends on I2C config SND_SOC_STAC9766 tristate + depends on SND_SOC_AC97_BUS config SND_SOC_STI_SAS tristate "codec Audio support for STI SAS codec" @@ -1281,6 +1340,15 @@ config SND_SOC_TLV320AIC3X config SND_SOC_TLV320DAC33 tristate + depends on I2C + +config SND_SOC_TLV320ADCX140 + tristate "Texas Instruments TLV320ADCX140 CODEC family" + depends on I2C + select REGMAP_I2C + help + Add support for Texas Instruments tlv320adc3140, tlv320adc5140 and + tlv320adc6140 quad channel ADCs. config SND_SOC_TS3A227E tristate "TI Headset/Mic detect and keypress chip" @@ -1301,11 +1369,13 @@ config SND_SOC_TSCS454 Add support for Tempo Semiconductor's TSCS454 audio CODEC. config SND_SOC_TWL4030 - select MFD_TWL4030_AUDIO tristate + depends on TWL4030_CORE + select MFD_TWL4030_AUDIO config SND_SOC_TWL6040 tristate + depends on TWL6040_CORE config SND_SOC_UDA1334 tristate "NXP UDA1334 DAC" @@ -1345,30 +1415,40 @@ config SND_SOC_WL1273 config SND_SOC_WM0010 tristate + depends on SPI_MASTER config SND_SOC_WM1250_EV1 tristate + depends on I2C config SND_SOC_WM2000 tristate + depends on I2C config SND_SOC_WM2200 tristate + depends on I2C config SND_SOC_WM5100 tristate + depends on I2C config SND_SOC_WM5102 tristate + depends on MFD_WM5102 config SND_SOC_WM5110 tristate + depends on MFD_WM5110 config SND_SOC_WM8350 tristate + depends on MFD_WM8350 config SND_SOC_WM8400 tristate + # FIXME nothing selects SND_SOC_WM8400?? + depends on MFD_WM8400 config SND_SOC_WM8510 tristate "Wolfson Microelectronics WM8510 CODEC" @@ -1456,9 +1536,11 @@ config SND_SOC_WM8904 config SND_SOC_WM8940 tristate + depends on I2C config SND_SOC_WM8955 tristate + depends on I2C config SND_SOC_WM8960 tristate "Wolfson Microelectronics WM8960 CODEC" @@ -1466,6 +1548,7 @@ config SND_SOC_WM8960 config SND_SOC_WM8961 tristate + depends on I2C config SND_SOC_WM8962 tristate "Wolfson Microelectronics WM8962 CODEC" @@ -1473,6 +1556,7 @@ config SND_SOC_WM8962 config SND_SOC_WM8971 tristate + depends on I2C config SND_SOC_WM8974 tristate "Wolfson Microelectronics WM8974 codec" @@ -1484,6 +1568,7 @@ config SND_SOC_WM8978 config SND_SOC_WM8983 tristate + depends on I2C config SND_SOC_WM8985 tristate "Wolfson Microelectronics WM8985 and WM8758 codec driver" @@ -1494,12 +1579,15 @@ config SND_SOC_WM8988 config SND_SOC_WM8990 tristate + depends on I2C config SND_SOC_WM8991 tristate + depends on I2C config SND_SOC_WM8993 tristate + depends on I2C config SND_SOC_WM8994 tristate @@ -1509,12 +1597,15 @@ config SND_SOC_WM8995 config SND_SOC_WM8996 tristate + depends on I2C config SND_SOC_WM8997 tristate + depends on MFD_WM8997 config SND_SOC_WM8998 tristate + depends on MFD_WM8998 config SND_SOC_WM9081 tristate @@ -1522,19 +1613,23 @@ config SND_SOC_WM9081 config SND_SOC_WM9090 tristate + depends on I2C config SND_SOC_WM9705 tristate + depends on SND_SOC_AC97_BUS select REGMAP_AC97 select AC97_BUS_COMPAT if AC97_BUS_NEW config SND_SOC_WM9712 tristate + depends on SND_SOC_AC97_BUS select REGMAP_AC97 select AC97_BUS_COMPAT if AC97_BUS_NEW config SND_SOC_WM9713 tristate + depends on SND_SOC_AC97_BUS select REGMAP_AC97 select AC97_BUS_COMPAT if AC97_BUS_NEW @@ -1555,6 +1650,7 @@ config SND_SOC_ZX_AUD96P22 # Amp config SND_SOC_LM4857 tristate + depends on I2C config SND_SOC_MAX9759 tristate "Maxim MAX9759 speaker Amplifier" @@ -1562,15 +1658,19 @@ config SND_SOC_MAX9759 config SND_SOC_MAX9768 tristate + depends on I2C config SND_SOC_MAX9877 tristate + depends on I2C config SND_SOC_MC13783 tristate + depends on MFD_MC13XXX config SND_SOC_ML26124 tristate + depends on I2C config SND_SOC_MT6351 tristate "MediaTek MT6351 Codec" @@ -1608,6 +1708,7 @@ config SND_SOC_NAU8824 config SND_SOC_NAU8825 tristate + depends on I2C config SND_SOC_TPA6130A2 tristate "Texas Instruments TPA6130A2 headphone amplifier" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ba1b4b3fa2da..03533157cda6 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -177,6 +177,7 @@ snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-rt5677-spi-objs := rt5677-spi.o snd-soc-rt5682-objs := rt5682.o +snd-soc-rt5682-sdw-objs := rt5682-sdw.o snd-soc-rt700-objs := rt700.o rt700-sdw.o snd-soc-rt711-objs := rt711.o rt711-sdw.o snd-soc-rt715-objs := rt715.o rt715-sdw.o @@ -218,6 +219,7 @@ snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320dac33-objs := tlv320dac33.o +snd-soc-tlv320adcx140-objs := tlv320adcx140.o snd-soc-tscs42xx-objs := tscs42xx.o snd-soc-tscs454-objs := tscs454.o snd-soc-ts3a227e-objs := ts3a227e.o @@ -476,6 +478,7 @@ obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o +obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o @@ -516,6 +519,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC32X4_I2C) += snd-soc-tlv320aic32x4-i2c.o obj-$(CONFIG_SND_SOC_TLV320AIC32X4_SPI) += snd-soc-tlv320aic32x4-spi.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o +obj-$(CONFIG_SND_SOC_TLV320ADCX140) += snd-soc-tlv320adcx140.o obj-$(CONFIG_SND_SOC_TSCS42XX) += snd-soc-tscs42xx.o obj-$(CONFIG_SND_SOC_TSCS454) += snd-soc-tscs454.o obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 6a24f570c5e8..d3dc42aa6825 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -45,6 +45,9 @@ struct cros_ec_codec_priv { /* DMIC */ atomic_t dmic_probed; + /* I2S_RX */ + uint32_t i2s_rx_bclk_ratio; + /* WoV */ bool wov_enabled; uint8_t *wov_audio_shm_p; @@ -259,6 +262,7 @@ static int i2s_rx_hw_params(struct snd_pcm_substream *substream, snd_soc_component_get_drvdata(component); struct ec_param_ec_codec_i2s_rx p; enum ec_codec_i2s_rx_sample_depth depth; + uint32_t bclk; int ret; if (params_rate(params) != 48000) @@ -284,15 +288,29 @@ static int i2s_rx_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - dev_dbg(component->dev, "set bclk to %u\n", - snd_soc_params_to_bclk(params)); + if (priv->i2s_rx_bclk_ratio) + bclk = params_rate(params) * priv->i2s_rx_bclk_ratio; + else + bclk = snd_soc_params_to_bclk(params); + + dev_dbg(component->dev, "set bclk to %u\n", bclk); p.cmd = EC_CODEC_I2S_RX_SET_BCLK; - p.set_bclk_param.bclk = snd_soc_params_to_bclk(params); + p.set_bclk_param.bclk = bclk; return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, (uint8_t *)&p, sizeof(p), NULL, 0); } +static int i2s_rx_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + priv->i2s_rx_bclk_ratio = ratio; + return 0; +} + static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; @@ -340,6 +358,7 @@ static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) static const struct snd_soc_dai_ops i2s_rx_dai_ops = { .hw_params = i2s_rx_hw_params, .set_fmt = i2s_rx_set_fmt, + .set_bclk_ratio = i2s_rx_set_bclk_ratio, }; static int i2s_rx_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 04b86a51e055..62f412d6f9f2 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -356,9 +356,9 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream, */ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - !dai->capture_active) || + !dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]) || (substream->stream == SNDRV_PCM_STREAM_CAPTURE && - !dai->playback_active)) { + !dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK])) { ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, CS4271_MODE2_PDN, CS4271_MODE2_PDN); diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c index e8840dc142ef..8d1869bf7f9c 100644 --- a/sound/soc/codecs/cs47l15.c +++ b/sound/soc/codecs/cs47l15.c @@ -1239,12 +1239,12 @@ static int cs47l15_open(struct snd_compr_stream *stream) struct madera *madera = priv->madera; int n_adsp; - if (strcmp(rtd->codec_dai->name, "cs47l15-dsp-trace") == 0) { + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l15-dsp-trace") == 0) { n_adsp = 0; } else { dev_err(madera->dev, "No suitable compressed stream for DAI '%s'\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); return -EINVAL; } diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 25bffc2968f0..6b0570f59630 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -1076,14 +1076,14 @@ static int cs47l24_open(struct snd_compr_stream *stream) struct arizona *arizona = priv->core.arizona; int n_adsp; - if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) { + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-voicectrl") == 0) { n_adsp = 2; - } else if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-trace") == 0) { + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-trace") == 0) { n_adsp = 1; } else { dev_err(arizona->dev, "No suitable compressed stream for DAI '%s'\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); return -EINVAL; } diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c index 3d48a0d9ecc5..18839807c9d1 100644 --- a/sound/soc/codecs/cs47l35.c +++ b/sound/soc/codecs/cs47l35.c @@ -1514,14 +1514,14 @@ static int cs47l35_open(struct snd_compr_stream *stream) struct madera *madera = priv->madera; int n_adsp; - if (strcmp(rtd->codec_dai->name, "cs47l35-dsp-voicectrl") == 0) { + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-voicectrl") == 0) { n_adsp = 2; - } else if (strcmp(rtd->codec_dai->name, "cs47l35-dsp-trace") == 0) { + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-trace") == 0) { n_adsp = 0; } else { dev_err(madera->dev, "No suitable compressed stream for DAI '%s'\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); return -EINVAL; } diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c index bef3471f482d..a575113207f0 100644 --- a/sound/soc/codecs/cs47l85.c +++ b/sound/soc/codecs/cs47l85.c @@ -2457,14 +2457,14 @@ static int cs47l85_open(struct snd_compr_stream *stream) struct madera *madera = priv->madera; int n_adsp; - if (strcmp(rtd->codec_dai->name, "cs47l85-dsp-voicectrl") == 0) { + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-voicectrl") == 0) { n_adsp = 5; - } else if (strcmp(rtd->codec_dai->name, "cs47l85-dsp-trace") == 0) { + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-trace") == 0) { n_adsp = 0; } else { dev_err(madera->dev, "No suitable compressed stream for DAI '%s'\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); return -EINVAL; } diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c index 266eade82764..81a1311b14e6 100644 --- a/sound/soc/codecs/cs47l90.c +++ b/sound/soc/codecs/cs47l90.c @@ -2368,14 +2368,14 @@ static int cs47l90_open(struct snd_compr_stream *stream) struct madera *madera = priv->madera; int n_adsp; - if (strcmp(rtd->codec_dai->name, "cs47l90-dsp-voicectrl") == 0) { + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-voicectrl") == 0) { n_adsp = 5; - } else if (strcmp(rtd->codec_dai->name, "cs47l90-dsp-trace") == 0) { + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-trace") == 0) { n_adsp = 0; } else { dev_err(madera->dev, "No suitable compressed stream for DAI '%s'\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); return -EINVAL; } diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c index 942040fd354f..15fc213d178d 100644 --- a/sound/soc/codecs/cs47l92.c +++ b/sound/soc/codecs/cs47l92.c @@ -1840,12 +1840,12 @@ static int cs47l92_open(struct snd_compr_stream *stream) struct madera *madera = priv->madera; int n_adsp; - if (strcmp(rtd->codec_dai->name, "cs47l92-dsp-trace") == 0) { + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l92-dsp-trace") == 0) { n_adsp = 0; } else { dev_err(madera->dev, "No suitable compressed stream for DAI '%s'\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); return -EINVAL; } diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index e6558475e006..fba9b749839d 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1998,11 +1998,11 @@ static struct hdac_hdmi_drv_data intel_drv_data = { static int hdac_hdmi_dev_probe(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi_priv = NULL; + struct hdac_hdmi_priv *hdmi_priv; struct snd_soc_dai_driver *hdmi_dais = NULL; - struct hdac_ext_link *hlink = NULL; + struct hdac_ext_link *hlink; int num_dais = 0; - int ret = 0; + int ret; struct hdac_driver *hdrv = drv_to_hdac_driver(hdev->dev.driver); const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv); diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c index 16313b973eaa..a8bd793a7867 100644 --- a/sound/soc/codecs/max98357a.c +++ b/sound/soc/codecs/max98357a.c @@ -5,6 +5,7 @@ */ #include <linux/acpi.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> #include <linux/gpio.h> @@ -24,26 +25,24 @@ struct max98357a_priv { unsigned int sdmode_delay; }; -static int max98357a_daiops_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) +static int max98357a_sdmode_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { - struct max98357a_priv *max98357a = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct max98357a_priv *max98357a = + snd_soc_component_get_drvdata(component); if (!max98357a->sdmode) return 0; - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - mdelay(max98357a->sdmode_delay); + if (event & SND_SOC_DAPM_POST_PMU) { + msleep(max98357a->sdmode_delay); gpiod_set_value(max98357a->sdmode, 1); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dev_dbg(component->dev, "set sdmode to 1"); + } else if (event & SND_SOC_DAPM_PRE_PMD) { gpiod_set_value(max98357a->sdmode, 0); - break; + dev_dbg(component->dev, "set sdmode to 0"); } return 0; @@ -51,10 +50,14 @@ static int max98357a_daiops_trigger(struct snd_pcm_substream *substream, static const struct snd_soc_dapm_widget max98357a_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("Speaker"), + SND_SOC_DAPM_OUT_DRV_E("SD_MODE", SND_SOC_NOPM, 0, 0, NULL, 0, + max98357a_sdmode_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), }; static const struct snd_soc_dapm_route max98357a_dapm_routes[] = { - {"Speaker", NULL, "HiFi Playback"}, + {"SD_MODE", NULL, "HiFi Playback"}, + {"Speaker", NULL, "SD_MODE"}, }; static const struct snd_soc_component_driver max98357a_component_driver = { @@ -68,10 +71,6 @@ static const struct snd_soc_component_driver max98357a_component_driver = { .non_legacy_dai_naming = 1, }; -static const struct snd_soc_dai_ops max98357a_dai_ops = { - .trigger = max98357a_daiops_trigger, -}; - static struct snd_soc_dai_driver max98357a_dai_driver = { .name = "HiFi", .playback = { @@ -91,7 +90,6 @@ static struct snd_soc_dai_driver max98357a_dai_driver = { .channels_min = 1, .channels_max = 2, }, - .ops = &max98357a_dai_ops, }; static int max98357a_platform_probe(struct platform_device *pdev) @@ -135,6 +133,7 @@ MODULE_DEVICE_TABLE(of, max98357a_device_id); #ifdef CONFIG_ACPI static const struct acpi_device_id max98357a_acpi_match[] = { { "MX98357A", 0 }, + { "MX98360A", 0 }, {}, }; MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match); diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index a36c416caad4..d1797003c83d 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -1,15 +1,13 @@ -// SPDX-License-Identifier: GPL-2.0 // +// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019 MediaTek Inc. #include <linux/module.h> #include <linux/kernel.h> -#include <linux/version.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/pm_runtime.h> #include <linux/delay.h> -#include <linux/debugfs.h> #include <sound/soc.h> #include <sound/tlv.h> #include <sound/pcm_params.h> @@ -225,14 +223,87 @@ static int _mt6660_chip_power_on(struct mt6660_chip *chip, int on_off) 0x01, on_off ? 0x00 : 0x01); } +struct reg_table { + uint32_t addr; + uint32_t mask; + uint32_t val; +}; + +static const struct reg_table mt6660_setting_table[] = { + { 0x20, 0x80, 0x00 }, + { 0x30, 0x01, 0x00 }, + { 0x50, 0x1c, 0x04 }, + { 0xB1, 0x0c, 0x00 }, + { 0xD3, 0x03, 0x03 }, + { 0xE0, 0x01, 0x00 }, + { 0x98, 0x44, 0x04 }, + { 0xB9, 0xff, 0x82 }, + { 0xB7, 0x7777, 0x7273 }, + { 0xB6, 0x07, 0x03 }, + { 0x6B, 0xe0, 0x20 }, + { 0x07, 0xff, 0x70 }, + { 0xBB, 0xff, 0x20 }, + { 0x69, 0xff, 0x40 }, + { 0xBD, 0xffff, 0x17f8 }, + { 0x70, 0xff, 0x15 }, + { 0x7C, 0xff, 0x00 }, + { 0x46, 0xff, 0x1d }, + { 0x1A, 0xffffffff, 0x7fdb7ffe }, + { 0x1B, 0xffffffff, 0x7fdb7ffe }, + { 0x51, 0xff, 0x58 }, + { 0xA2, 0xff, 0xce }, + { 0x33, 0xffff, 0x7fff }, + { 0x4C, 0xffff, 0x0116 }, + { 0x16, 0x1800, 0x0800 }, + { 0x68, 0x1f, 0x07 }, +}; + +static int mt6660_component_setting(struct snd_soc_component *component) +{ + struct mt6660_chip *chip = snd_soc_component_get_drvdata(component); + int ret = 0; + size_t i = 0; + + ret = _mt6660_chip_power_on(chip, 1); + if (ret < 0) { + dev_err(component->dev, "%s chip power on failed\n", __func__); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(mt6660_setting_table); i++) { + ret = snd_soc_component_update_bits(component, + mt6660_setting_table[i].addr, + mt6660_setting_table[i].mask, + mt6660_setting_table[i].val); + if (ret < 0) { + dev_err(component->dev, "%s update 0x%02x failed\n", + __func__, mt6660_setting_table[i].addr); + return ret; + } + } + + ret = _mt6660_chip_power_on(chip, 0); + if (ret < 0) { + dev_err(component->dev, "%s chip power off failed\n", __func__); + return ret; + } + + return 0; +} + static int mt6660_component_probe(struct snd_soc_component *component) { struct mt6660_chip *chip = snd_soc_component_get_drvdata(component); + int ret; dev_dbg(component->dev, "%s\n", __func__); snd_soc_component_init_regmap(component, chip->regmap); - return 0; + ret = mt6660_component_setting(component); + if (ret < 0) + dev_err(chip->dev, "mt6660 component setting failed\n"); + + return ret; } static void mt6660_component_remove(struct snd_soc_component *component) @@ -506,4 +577,4 @@ module_i2c_driver(mt6660_i2c_driver); MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>"); MODULE_DESCRIPTION("MT6660 SPKAMP Driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION("1.0.7_G"); +MODULE_VERSION("1.0.8_G"); diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c index 287c962ba00d..115706a55577 100644 --- a/sound/soc/codecs/rk3328_codec.c +++ b/sound/soc/codecs/rk3328_codec.c @@ -7,6 +7,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -31,7 +32,7 @@ struct rk3328_codec_priv { struct regmap *regmap; - struct regmap *grf; + struct gpio_desc *mute; struct clk *mclk; struct clk *pclk; unsigned int sclk; @@ -106,16 +107,6 @@ static int rk3328_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static void rk3328_analog_output(struct rk3328_codec_priv *rk3328, int mute) -{ - unsigned int val = BIT(17); - - if (mute) - val |= BIT(1); - - regmap_write(rk3328->grf, RK3328_GRF_SOC_CON10, val); -} - static int rk3328_digital_mute(struct snd_soc_dai *dai, int mute) { struct rk3328_codec_priv *rk3328 = @@ -205,7 +196,7 @@ static int rk3328_codec_open_playback(struct rk3328_codec_priv *rk3328) } msleep(rk3328->spk_depop_time); - rk3328_analog_output(rk3328, 1); + gpiod_set_value(rk3328->mute, 0); regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL, HPOUTL_GAIN_MASK, OUT_VOLUME); @@ -246,7 +237,7 @@ static int rk3328_codec_close_playback(struct rk3328_codec_priv *rk3328) { size_t i; - rk3328_analog_output(rk3328, 0); + gpiod_set_value(rk3328->mute, 1); regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL, HPOUTL_GAIN_MASK, 0); @@ -446,7 +437,6 @@ static int rk3328_platform_probe(struct platform_device *pdev) dev_err(&pdev->dev, "missing 'rockchip,grf'\n"); return PTR_ERR(grf); } - rk3328->grf = grf; /* enable i2s_acodec_en */ regmap_write(grf, RK3328_GRF_SOC_CON2, (BIT(14) << 16 | BIT(14))); @@ -458,7 +448,18 @@ static int rk3328_platform_probe(struct platform_device *pdev) rk3328->spk_depop_time = 200; } - rk3328_analog_output(rk3328, 0); + rk3328->mute = gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_HIGH); + if (IS_ERR(rk3328->mute)) + return PTR_ERR(rk3328->mute); + /* + * Rock64 is the only supported platform to have widely relied on + * this; if we do happen to come across an old DTB, just leave the + * external mute forced off. + */ + if (!rk3328->mute && of_machine_is_compatible("pine64,rock64")) { + dev_warn(&pdev->dev, "assuming implicit control of GPIO_MUTE; update devicetree if possible\n"); + regmap_write(grf, RK3328_GRF_SOC_CON10, BIT(17) | BIT(1)); + } rk3328->mclk = devm_clk_get(&pdev->dev, "mclk"); if (IS_ERR(rk3328->mclk)) diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c index a887d5ccb10d..d181c217d835 100644 --- a/sound/soc/codecs/rl6231.c +++ b/sound/soc/codecs/rl6231.c @@ -102,6 +102,7 @@ struct pll_calc_map { static const struct pll_calc_map pll_preset_table[] = { {19200000, 4096000, 23, 14, 1, false}, {19200000, 24576000, 3, 30, 3, false}, + {3840000, 24576000, 3, 30, 0, true}, }; static unsigned int find_best_div(unsigned int in, diff --git a/sound/soc/codecs/rl6231.h b/sound/soc/codecs/rl6231.h index 31a9643b0afd..6d8ed0377296 100644 --- a/sound/soc/codecs/rl6231.h +++ b/sound/soc/codecs/rl6231.h @@ -10,7 +10,7 @@ #ifndef __RL6231_H__ #define __RL6231_H__ -#define RL6231_PLL_INP_MAX 40000000 +#define RL6231_PLL_INP_MAX 50000000 #define RL6231_PLL_INP_MIN 256000 #define RL6231_PLL_N_MAX 0x1ff #define RL6231_PLL_K_MAX 0x1f diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 66eb55b4ffd4..bb310bc7febd 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -444,7 +444,7 @@ static int rt1015_boost_mode_put(struct snd_kcontrol *kcontrol, return 0; } -static int rt5518_bypass_boost_get(struct snd_kcontrol *kcontrol, +static int rt1015_bypass_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = @@ -457,7 +457,7 @@ static int rt5518_bypass_boost_get(struct snd_kcontrol *kcontrol, return 0; } -static int rt5518_bypass_boost_put(struct snd_kcontrol *kcontrol, +static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = @@ -497,7 +497,7 @@ static const struct snd_kcontrol_new rt1015_snd_controls[] = { rt1015_boost_mode_get, rt1015_boost_mode_put), SOC_ENUM("Mono LR Select", rt1015_mono_lr_sel), SOC_SINGLE_EXT("Bypass Boost", SND_SOC_NOPM, 0, 1, 0, - rt5518_bypass_boost_get, rt5518_bypass_boost_put), + rt1015_bypass_boost_get, rt1015_bypass_boost_put), }; static int rt1015_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, @@ -841,12 +841,12 @@ static void rt1015_remove(struct snd_soc_component *component) #define RT1015_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) -struct snd_soc_dai_ops rt1015_aif_dai_ops = { +static struct snd_soc_dai_ops rt1015_aif_dai_ops = { .hw_params = rt1015_hw_params, .set_fmt = rt1015_set_dai_fmt, }; -struct snd_soc_dai_driver rt1015_dai[] = { +static struct snd_soc_dai_driver rt1015_dai[] = { { .name = "rt1015-aif", .id = 0, diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index d930f60cb797..a5a7e46de246 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -507,6 +507,28 @@ static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream, kfree(stream); } +static int rt1308_sdw_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); + + if (tx_mask) + return -EINVAL; + + if (slots > 2) + return -EINVAL; + + rt1308->rx_mask = rx_mask; + rt1308->slots = slots; + /* slot_width is not used since it's irrelevant for SoundWire */ + + return 0; +} + static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { @@ -517,7 +539,7 @@ static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream, struct sdw_port_config port_config; enum sdw_data_direction direction; struct sdw_stream_data *stream; - int retval, port, num_channels; + int retval, port, num_channels, ch_mask; dev_dbg(dai->dev, "%s %s", __func__, dai->name); stream = snd_soc_dai_get_dma_data(dai, substream); @@ -537,13 +559,20 @@ static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + if (rt1308->slots) { + num_channels = rt1308->slots; + ch_mask = rt1308->rx_mask; + } else { + num_channels = params_channels(params); + ch_mask = (1 << num_channels) - 1; + } + stream_config.frame_rate = params_rate(params); - stream_config.ch_count = params_channels(params); + stream_config.ch_count = num_channels; stream_config.bps = snd_pcm_format_width(params_format(params)); stream_config.direction = direction; - num_channels = params_channels(params); - port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.ch_mask = ch_mask; port_config.num = port; retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config, @@ -597,6 +626,7 @@ static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { .hw_free = rt1308_sdw_pcm_hw_free, .set_sdw_stream = rt1308_set_sdw_stream, .shutdown = rt1308_sdw_shutdown, + .set_tdm_slot = rt1308_sdw_set_tdm_slot, }; #define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000 diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h index c9341e70d6cf..c5ce75666dcc 100644 --- a/sound/soc/codecs/rt1308-sdw.h +++ b/sound/soc/codecs/rt1308-sdw.h @@ -160,6 +160,8 @@ struct rt1308_sdw_priv { struct sdw_bus_params params; bool hw_init; bool first_hw_init; + int rx_mask; + int slots; }; struct sdw_stream_data { diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index e66d08398f74..89e0f58512fa 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -1604,7 +1604,7 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); - int pd, idx = -EINVAL; + int pd, idx; pd = rl6231_get_pre_div(rt5659->regmap, RT5659_ADDA_CLK_1, RT5659_I2S_PD1_SFT); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c new file mode 100644 index 000000000000..a2d1d3ae1e31 --- /dev/null +++ b/sound/soc/codecs/rt5682-sdw.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt5682-sdw.c -- RT5682 ALSA SoC audio component driver +// +// Copyright 2019 Realtek Semiconductor Corp. +// Author: Oder Chiou <oder_chiou@realtek.com> +// + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/acpi.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/mutex.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "rt5682.h" +#include "rt5682-sdw.h" + +static bool rt5682_sdw_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x3000: + case 0x3001: + case 0x3004: + case 0x3005: + case 0x3008: + return true; + default: + return false; + } +} + +const struct regmap_config rt5682_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, + .val_bits = 8, + .max_register = RT5682_I2C_MODE, + .readable_reg = rt5682_sdw_readable_register, + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt5682_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt5682->status = status; + + if (status == SDW_SLAVE_UNATTACHED) + rt5682->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt5682->hw_init || rt5682->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt5682_io_init(&slave->dev, slave); +} + +static int rt5682_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i, num_of_ports = 1; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x4; /* BITMAP: 00000100 */ + prop->sink_ports = 0x2; /* BITMAP: 00000010 */ + + nval = hweight32(prop->source_ports); + num_of_ports += nval; + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + num_of_ports += nval; + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* Allocate port_ready based on num_of_ports */ + slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, + sizeof(*slave->port_ready), + GFP_KERNEL); + if (!slave->port_ready) + return -ENOMEM; + + /* Initialize completion */ + for (i = 0; i < num_of_ports; i++) + init_completion(&slave->port_ready[i]); + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +/* Bus clock frequency */ +#define RT5682_CLK_FREQ_9600000HZ 9600000 +#define RT5682_CLK_FREQ_12000000HZ 12000000 +#define RT5682_CLK_FREQ_6000000HZ 6000000 +#define RT5682_CLK_FREQ_4800000HZ 4800000 +#define RT5682_CLK_FREQ_2400000HZ 2400000 +#define RT5682_CLK_FREQ_12288000HZ 12288000 + +static int rt5682_clock_config(struct device *dev) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt5682->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT5682_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT5682_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT5682_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT5682_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT5682_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT5682_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt5682->sdw_regmap, 0xe0, value); + regmap_write(rt5682->sdw_regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +static int rt5682_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt5682->params, params, sizeof(*params)); + + ret = rt5682_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt5682_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + if (status->control_port & 0x4) { + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + } + + return 0; +} + +static struct sdw_slave_ops rt5682_slave_ops = { + .read_prop = rt5682_read_prop, + .interrupt_callback = rt5682_interrupt_callback, + .update_status = rt5682_update_status, + .bus_config = rt5682_bus_config, +}; + +static int rt5682_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Assign ops */ + slave->ops = &rt5682_slave_ops; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt5682_sdw_regmap); + if (IS_ERR(regmap)) + return -EINVAL; + + rt5682_sdw_init(&slave->dev, regmap, slave); + + return 0; +} + +static int rt5682_sdw_remove(struct sdw_slave *slave) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + + if (rt5682 && rt5682->hw_init) + cancel_delayed_work(&rt5682->jack_detect_work); + + return 0; +} + +static const struct sdw_device_id rt5682_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x5682, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt5682_id); + +static int __maybe_unused rt5682_dev_suspend(struct device *dev) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + + if (!rt5682->hw_init) + return 0; + + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); + + return 0; +} + +static int __maybe_unused rt5682_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt5682->hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT5682_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt5682->regmap, false); + regcache_sync(rt5682->regmap); + + return 0; +} + +static const struct dev_pm_ops rt5682_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume) + SET_RUNTIME_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume, NULL) +}; + +static struct sdw_driver rt5682_sdw_driver = { + .driver = { + .name = "rt5682", + .owner = THIS_MODULE, + .pm = &rt5682_pm, + }, + .probe = rt5682_sdw_probe, + .remove = rt5682_sdw_remove, + .ops = &rt5682_slave_ops, + .id_table = rt5682_id, +}; +module_sdw_driver(rt5682_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT5682 driver SDW"); +MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5682-sdw.h b/sound/soc/codecs/rt5682-sdw.h new file mode 100644 index 000000000000..76e6f607066e --- /dev/null +++ b/sound/soc/codecs/rt5682-sdw.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * rt5682-sdw.h -- RT5682 SDW ALSA SoC audio driver + * + * Copyright 2019 Realtek Semiconductor Corp. + * Author: Oder Chiou <oder_chiou@realtek.com> + */ + +#ifndef __RT5682_SDW_H__ +#define __RT5682_SDW_H__ + +#define RT5682_SDW_ADDR_L 0x3000 +#define RT5682_SDW_ADDR_H 0x3001 +#define RT5682_SDW_DATA_L 0x3004 +#define RT5682_SDW_DATA_H 0x3005 +#define RT5682_SDW_CMD 0x3008 + +#define RT5682_PROBE_TIMEOUT 2000 + +#endif /* __RT5682_SDW_H__ */ diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index ae6f6121bc1b..c9268a230daa 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -11,13 +11,13 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> #include <linux/gpio.h> #include <linux/of_gpio.h> -#include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> @@ -31,8 +31,7 @@ #include "rl6231.h" #include "rt5682.h" - -#define RT5682_NUM_SUPPLIES 3 +#include "rt5682-sdw.h" static const char *rt5682_supply_names[RT5682_NUM_SUPPLIES] = { "AVDD", @@ -45,35 +44,15 @@ static const struct rt5682_platform_data i2s_default_platform_data = { .dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3, .jd_src = RT5682_JD1, .btndet_delay = 16, -}; - -struct rt5682_priv { - struct snd_soc_component *component; - struct rt5682_platform_data pdata; - struct regmap *regmap; - struct snd_soc_jack *hs_jack; - struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES]; - struct delayed_work jack_detect_work; - struct delayed_work jd_check_work; - struct mutex calibrate_mutex; - - int sysclk; - int sysclk_src; - int lrck[RT5682_AIFS]; - int bclk[RT5682_AIFS]; - int master[RT5682_AIFS]; - - int pll_src; - int pll_in; - int pll_out; - - int jack_type; + .dai_clk_names[RT5682_DAI_WCLK_IDX] = "rt5682-dai-wclk", + .dai_clk_names[RT5682_DAI_BCLK_IDX] = "rt5682-dai-bclk", }; static const struct reg_sequence patch_list[] = { {RT5682_HP_IMP_SENS_CTRL_19, 0x1000}, {RT5682_DAC_ADC_DIG_VOL1, 0xa020}, {RT5682_I2C_CTRL, 0x000f}, + {RT5682_PLL2_INTERNAL, 0x8266}, }; static const struct reg_default rt5682_reg[] = { @@ -221,7 +200,7 @@ static const struct reg_default rt5682_reg[] = { {0x0148, 0x0000}, {0x0149, 0x0000}, {0x0150, 0x79a1}, - {0x0151, 0x0000}, + {0x0156, 0xaaaa}, {0x0160, 0x4ec0}, {0x0161, 0x0080}, {0x0162, 0x0200}, @@ -805,10 +784,27 @@ static const struct snd_kcontrol_new rt5682_if1_45_adc_swap_mux = static const struct snd_kcontrol_new rt5682_if1_67_adc_swap_mux = SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682_if1_67_adc_enum); -static void rt5682_reset(struct regmap *regmap) +static const char * const rt5682_dac_select[] = { + "IF1", "SOUND" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682_dacl_enum, + RT5682_AD_DA_MIXER, RT5682_DAC1_L_SEL_SFT, rt5682_dac_select); + +static const struct snd_kcontrol_new rt5682_dac_l_mux = + SOC_DAPM_ENUM("DAC L Mux", rt5682_dacl_enum); + +static SOC_ENUM_SINGLE_DECL(rt5682_dacr_enum, + RT5682_AD_DA_MIXER, RT5682_DAC1_R_SEL_SFT, rt5682_dac_select); + +static const struct snd_kcontrol_new rt5682_dac_r_mux = + SOC_DAPM_ENUM("DAC R Mux", rt5682_dacr_enum); + +static void rt5682_reset(struct rt5682_priv *rt5682) { - regmap_write(regmap, RT5682_RESET, 0); - regmap_write(regmap, RT5682_I2C_MODE, 1); + regmap_write(rt5682->regmap, RT5682_RESET, 0); + if (!rt5682->is_sdw) + regmap_write(rt5682->regmap, RT5682_I2C_MODE, 1); } /** * rt5682_sel_asrc_clk_src - select ASRC clock source for a set of filters @@ -871,6 +867,8 @@ static int rt5682_button_detect(struct snd_soc_component *component) static void rt5682_enable_push_button_irq(struct snd_soc_component *component, bool enable) { + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + if (enable) { snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_EN); @@ -880,8 +878,15 @@ static void rt5682_enable_push_button_irq(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, RT5682_4BTN_IL_MASK | RT5682_4BTN_IL_RST_MASK, RT5682_4BTN_IL_EN | RT5682_4BTN_IL_NOR); - snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, - RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_EN); + if (rt5682->is_sdw) + snd_soc_component_update_bits(component, + RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK | RT5682_IL_IRQ_TYPE_MASK, + RT5682_IL_IRQ_EN | RT5682_IL_IRQ_PUL); + else + snd_soc_component_update_bits(component, + RT5682_IRQ_CTRL_3, RT5682_IL_IRQ_MASK, + RT5682_IL_IRQ_EN); } else { snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_DIS); @@ -909,6 +914,7 @@ static int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = &component->dapm; unsigned int val, count; if (jack_insert) { @@ -917,10 +923,10 @@ static int rt5682_headset_detect(struct snd_soc_component *component, RT5682_PWR_VREF2 | RT5682_PWR_MB, RT5682_PWR_VREF2 | RT5682_PWR_MB); snd_soc_component_update_bits(component, - RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0); + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0); usleep_range(15000, 20000); snd_soc_component_update_bits(component, - RT5682_PWR_ANLG_1, RT5682_PWR_FV2, RT5682_PWR_FV2); + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, RT5682_PWR_FV2); snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3, RT5682_PWR_CBJ, RT5682_PWR_CBJ); @@ -951,8 +957,13 @@ static int rt5682_headset_detect(struct snd_soc_component *component, rt5682_enable_push_button_irq(component, false); snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW); - snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, - RT5682_PWR_VREF2 | RT5682_PWR_MB, 0); + if (snd_soc_dapm_get_pin_status(dapm, "MICBIAS")) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0); + else + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, + RT5682_PWR_VREF2 | RT5682_PWR_MB, 0); snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3, RT5682_PWR_CBJ, 0); @@ -999,62 +1010,69 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component, rt5682->hs_jack = hs_jack; - if (!hs_jack) { - regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, - RT5682_JD1_EN_MASK, RT5682_JD1_DIS); - regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, - RT5682_POW_JDH | RT5682_POW_JDL, 0); - cancel_delayed_work_sync(&rt5682->jack_detect_work); - return 0; - } + if (!rt5682->is_sdw) { + if (!hs_jack) { + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK, RT5682_JD1_DIS); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_JDH | RT5682_POW_JDL, 0); + cancel_delayed_work_sync(&rt5682->jack_detect_work); + return 0; + } - switch (rt5682->pdata.jd_src) { - case RT5682_JD1: - snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_2, - RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); - snd_soc_component_write(component, RT5682_CBJ_CTRL_1, 0xd042); - snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_3, - RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN); - snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, - RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN); - regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, - RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ); - regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + switch (rt5682->pdata.jd_src) { + case RT5682_JD1: + snd_soc_component_update_bits(component, + RT5682_CBJ_CTRL_2, RT5682_EXT_JD_SRC, + RT5682_EXT_JD_SRC_MANUAL); + snd_soc_component_write(component, RT5682_CBJ_CTRL_1, + 0xd042); + snd_soc_component_update_bits(component, + RT5682_CBJ_CTRL_3, RT5682_CBJ_IN_BUF_EN, + RT5682_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, + RT5682_SAR_IL_CMD_1, RT5682_SAR_POW_MASK, + RT5682_SAR_POW_EN); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, RT5682_POW_IRQ | RT5682_POW_JDH | RT5682_POW_ANA, RT5682_POW_IRQ | RT5682_POW_JDH | RT5682_POW_ANA); - regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, - RT5682_PWR_JDH | RT5682_PWR_JDL, - RT5682_PWR_JDH | RT5682_PWR_JDL); - regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, - RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK, - RT5682_JD1_EN | RT5682_JD1_POL_NOR); - regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4, - 0x7f7f, (rt5682->pdata.btndet_delay << 8 | - rt5682->pdata.btndet_delay)); - regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5, - 0x7f7f, (rt5682->pdata.btndet_delay << 8 | - rt5682->pdata.btndet_delay)); - regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6, - 0x7f7f, (rt5682->pdata.btndet_delay << 8 | - rt5682->pdata.btndet_delay)); - regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7, - 0x7f7f, (rt5682->pdata.btndet_delay << 8 | - rt5682->pdata.btndet_delay)); - mod_delayed_work(system_power_efficient_wq, - &rt5682->jack_detect_work, msecs_to_jiffies(250)); - break; + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, + RT5682_PWR_JDH | RT5682_PWR_JDL, + RT5682_PWR_JDH | RT5682_PWR_JDL); + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK, + RT5682_JD1_EN | RT5682_JD1_POL_NOR); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, + msecs_to_jiffies(250)); + break; - case RT5682_JD_NULL: - regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, - RT5682_JD1_EN_MASK, RT5682_JD1_DIS); - regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, - RT5682_POW_JDH | RT5682_POW_JDL, 0); - break; + case RT5682_JD_NULL: + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK, RT5682_JD1_DIS); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_JDH | RT5682_POW_JDL, 0); + break; - default: - dev_warn(component->dev, "Wrong JD source\n"); - break; + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } } return 0; @@ -1134,11 +1152,13 @@ static void rt5682_jack_detect_handler(struct work_struct *work) SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3); - if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3)) - schedule_delayed_work(&rt5682->jd_check_work, 0); - else - cancel_delayed_work_sync(&rt5682->jd_check_work); + if (!rt5682->is_sdw) { + if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5682->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5682->jd_check_work); + } mutex_unlock(&rt5682->calibrate_mutex); } @@ -1146,7 +1166,7 @@ static void rt5682_jack_detect_handler(struct work_struct *work) static const struct snd_kcontrol_new rt5682_snd_controls[] = { /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL, - RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 86, 0, dac_vol_tlv), + RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 87, 0, dac_vol_tlv), /* IN Boost Volume */ SOC_SINGLE_TLV("CBJ Boost Volume", RT5682_CBJ_BST_CTRL, @@ -1177,11 +1197,11 @@ static int rt5682_div_sel(struct rt5682_priv *rt5682, } for (i = 0; i < size - 1; i++) { - pr_info("div[%d]=%d\n", i, div[i]); + dev_dbg(rt5682->component->dev, "div[%d]=%d\n", i, div[i]); if (target * div[i] == rt5682->sysclk) return i; if (target * div[i + 1] > rt5682->sysclk) { - pr_err("can't find div for sysclk %d\n", + dev_dbg(rt5682->component->dev, "can't find div for sysclk %d\n", rt5682->sysclk); return i; } @@ -1211,10 +1231,13 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); - int idx = -EINVAL; + int idx = -EINVAL, dmic_clk_rate = 3072000; static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128}; - idx = rt5682_div_sel(rt5682, 1500000, div, ARRAY_SIZE(div)); + if (rt5682->pdata.dmic_clk_rate) + dmic_clk_rate = rt5682->pdata.dmic_clk_rate; + + idx = rt5682_div_sel(rt5682, dmic_clk_rate, div, ARRAY_SIZE(div)); snd_soc_component_update_bits(component, RT5682_DMIC_CTRL_1, RT5682_DMIC_CLK_MASK, idx << RT5682_DMIC_CLK_SFT); @@ -1232,6 +1255,9 @@ static int set_filter_clk(struct snd_soc_dapm_widget *w, static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48}; + if (rt5682->is_sdw) + return 0; + val = snd_soc_component_read32(component, RT5682_GPIO_CTRL_1) & RT5682_GP4_PIN_MASK; if (w->shift == RT5682_PWR_ADC_S1F_BIT && @@ -1278,6 +1304,21 @@ static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w, return 0; } +static int is_sys_clk_from_pll2(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read32(component, RT5682_GLB_CLK); + val &= RT5682_SCLK_SRC_MASK; + if (val == RT5682_SCLK_SRC_PLL2) + return 1; + else + return 0; +} + static int is_using_asrc(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_widget *sink) { @@ -1503,10 +1544,18 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w, static int set_dmic_power(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 rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int delay = 50; + + if (rt5682->pdata.dmic_delay) + delay = rt5682->pdata.dmic_delay; + switch (event) { case SND_SOC_DAPM_POST_PMU: /*Add delay to avoid pop noise*/ - msleep(150); + msleep(delay); break; default: @@ -1516,7 +1565,7 @@ static int set_dmic_power(struct snd_soc_dapm_widget *w, return 0; } -static int rt5655_set_verf(struct snd_soc_dapm_widget *w, +static int rt5682_set_verf(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = @@ -1592,9 +1641,12 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL2B", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2B_BIT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("PLL2F", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2F_BIT, - 0, NULL, 0), + 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_SUPPLY("Vref1", RT5682_PWR_ANLG_1, RT5682_PWR_VREF1_BIT, 0, - rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + rt5682_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref2", RT5682_PWR_ANLG_1, RT5682_PWR_VREF2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, NULL, 0), /* ASRC */ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682_PLL_TRACK_1, @@ -1686,6 +1738,8 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SOUND DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SOUND DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), /* Digital Interface Select */ SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, @@ -1702,12 +1756,19 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, &rt5682_adcdat_pin_ctrl), + SND_SOC_DAPM_MUX("DAC L Mux", SND_SOC_NOPM, 0, 0, + &rt5682_dac_l_mux), + SND_SOC_DAPM_MUX("DAC R Mux", SND_SOC_NOPM, 0, 0, + &rt5682_dac_r_mux), + /* Audio Interface */ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, RT5682_I2S1_SDP, RT5682_SEL_ADCDAT_SFT, 1), SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, RT5682_I2S2_SDP, RT5682_I2S2_PIN_CFG_SFT, 1), SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDWRX", "SDW Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SDWTX", "SDW Capture", 0, SND_SOC_NOPM, 0, 0), /* Output Side */ /* DAC mixer before sound effect */ @@ -1776,7 +1837,11 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { /*PLL*/ {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + {"ADC Stereo1 Filter", NULL, "PLL2B", is_sys_clk_from_pll2}, + {"ADC Stereo1 Filter", NULL, "PLL2F", is_sys_clk_from_pll2}, {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + {"DAC Stereo1 Filter", NULL, "PLL2B", is_sys_clk_from_pll2}, + {"DAC Stereo1 Filter", NULL, "PLL2F", is_sys_clk_from_pll2}, /*ASRC*/ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, @@ -1860,8 +1925,8 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"}, {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"}, {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"}, - {"IF1_ADC Mux", NULL, "I2S1"}, {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"}, + {"AIF1TX", NULL, "I2S1"}, {"AIF1TX", NULL, "ADCDAT Mux"}, {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, @@ -1870,6 +1935,10 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"}, {"AIF2TX", NULL, "ADCDAT Mux"}, + {"SDWTX", NULL, "PLL2B"}, + {"SDWTX", NULL, "PLL2F"}, + {"SDWTX", NULL, "ADCDAT Mux"}, + {"IF1 DAC1 L", NULL, "AIF1RX"}, {"IF1 DAC1 L", NULL, "I2S1"}, {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"}, @@ -1877,10 +1946,24 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"IF1 DAC1 R", NULL, "I2S1"}, {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"}, + {"SOUND DAC L", NULL, "SDWRX"}, + {"SOUND DAC L", NULL, "DAC Stereo1 Filter"}, + {"SOUND DAC L", NULL, "PLL2B"}, + {"SOUND DAC L", NULL, "PLL2F"}, + {"SOUND DAC R", NULL, "SDWRX"}, + {"SOUND DAC R", NULL, "DAC Stereo1 Filter"}, + {"SOUND DAC R", NULL, "PLL2B"}, + {"SOUND DAC R", NULL, "PLL2F"}, + + {"DAC L Mux", "IF1", "IF1 DAC1 L"}, + {"DAC L Mux", "SOUND", "SOUND DAC L"}, + {"DAC R Mux", "IF1", "IF1 DAC1 R"}, + {"DAC R Mux", "SOUND", "SOUND DAC R"}, + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, - {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"}, + {"DAC1 MIXL", "DAC1 Switch", "DAC L Mux"}, {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, - {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"}, + {"DAC1 MIXR", "DAC1 Switch", "DAC R Mux"}, {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, @@ -2033,8 +2116,10 @@ static int rt5682_hw_params(struct snd_pcm_substream *substream, RT5682_I2S1_DL_MASK, len_1); if (rt5682->master[RT5682_AIF1]) { snd_soc_component_update_bits(component, - RT5682_ADDA_CLK_1, RT5682_I2S_M_DIV_MASK, - pre_div << RT5682_I2S_M_DIV_SFT); + RT5682_ADDA_CLK_1, RT5682_I2S_M_DIV_MASK | + RT5682_I2S_CLK_SRC_MASK, + pre_div << RT5682_I2S_M_DIV_SFT | + (rt5682->sysclk_src) << RT5682_I2S_CLK_SRC_SFT); } if (params_channels(params) == 1) /* mono mode */ snd_soc_component_update_bits(component, @@ -2207,61 +2292,157 @@ static int rt5682_set_component_pll(struct snd_soc_component *component, unsigned int freq_out) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); - struct rl6231_pll_code pll_code; + struct rl6231_pll_code pll_code, pll2f_code, pll2b_code; + unsigned int pll2_fout1; int ret; - if (source == rt5682->pll_src && freq_in == rt5682->pll_in && - freq_out == rt5682->pll_out) + if (source == rt5682->pll_src[pll_id] && + freq_in == rt5682->pll_in[pll_id] && + freq_out == rt5682->pll_out[pll_id]) return 0; if (!freq_in || !freq_out) { dev_dbg(component->dev, "PLL disabled\n"); - rt5682->pll_in = 0; - rt5682->pll_out = 0; + rt5682->pll_in[pll_id] = 0; + rt5682->pll_out[pll_id] = 0; snd_soc_component_update_bits(component, RT5682_GLB_CLK, RT5682_SCLK_SRC_MASK, RT5682_SCLK_SRC_MCLK); return 0; } - switch (source) { - case RT5682_PLL1_S_MCLK: - snd_soc_component_update_bits(component, RT5682_GLB_CLK, - RT5682_PLL1_SRC_MASK, RT5682_PLL1_SRC_MCLK); - break; - case RT5682_PLL1_S_BCLK1: - snd_soc_component_update_bits(component, RT5682_GLB_CLK, - RT5682_PLL1_SRC_MASK, RT5682_PLL1_SRC_BCLK1); - break; - default: - dev_err(component->dev, "Unknown PLL Source %d\n", source); - return -EINVAL; - } + if (pll_id == RT5682_PLL2) { + switch (source) { + case RT5682_PLL2_S_MCLK: + snd_soc_component_update_bits(component, + RT5682_GLB_CLK, RT5682_PLL2_SRC_MASK, + RT5682_PLL2_SRC_MCLK); + break; + default: + dev_err(component->dev, "Unknown PLL2 Source %d\n", + source); + return -EINVAL; + } - ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); - if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); - return ret; + /** + * PLL2 concatenates 2 PLL units. + * We suggest the Fout of the front PLL is 3.84MHz. + */ + pll2_fout1 = 3840000; + ret = rl6231_pll_calc(freq_in, pll2_fout1, &pll2f_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", + freq_in); + return ret; + } + dev_dbg(component->dev, "PLL2F: fin=%d fout=%d bypass=%d m=%d n=%d k=%d\n", + freq_in, pll2_fout1, + pll2f_code.m_bp, + (pll2f_code.m_bp ? 0 : pll2f_code.m_code), + pll2f_code.n_code, pll2f_code.k_code); + + ret = rl6231_pll_calc(pll2_fout1, freq_out, &pll2b_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", + pll2_fout1); + return ret; + } + dev_dbg(component->dev, "PLL2B: fin=%d fout=%d bypass=%d m=%d n=%d k=%d\n", + pll2_fout1, freq_out, + pll2b_code.m_bp, + (pll2b_code.m_bp ? 0 : pll2b_code.m_code), + pll2b_code.n_code, pll2b_code.k_code); + + snd_soc_component_write(component, RT5682_PLL2_CTRL_1, + pll2f_code.k_code << RT5682_PLL2F_K_SFT | + pll2b_code.k_code << RT5682_PLL2B_K_SFT | + pll2b_code.m_code); + snd_soc_component_write(component, RT5682_PLL2_CTRL_2, + pll2f_code.m_code << RT5682_PLL2F_M_SFT | + pll2b_code.n_code); + snd_soc_component_write(component, RT5682_PLL2_CTRL_3, + pll2f_code.n_code << RT5682_PLL2F_N_SFT); + snd_soc_component_update_bits(component, RT5682_PLL2_CTRL_4, + RT5682_PLL2B_M_BP_MASK | RT5682_PLL2F_M_BP_MASK | 0xf, + (pll2b_code.m_bp ? 1 : 0) << RT5682_PLL2B_M_BP_SFT | + (pll2f_code.m_bp ? 1 : 0) << RT5682_PLL2F_M_BP_SFT | + 0xf); + } else { + switch (source) { + case RT5682_PLL1_S_MCLK: + snd_soc_component_update_bits(component, + RT5682_GLB_CLK, RT5682_PLL1_SRC_MASK, + RT5682_PLL1_SRC_MCLK); + break; + case RT5682_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, + RT5682_GLB_CLK, RT5682_PLL1_SRC_MASK, + RT5682_PLL1_SRC_BCLK1); + break; + default: + dev_err(component->dev, "Unknown PLL1 Source %d\n", + source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", + freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5682_PLL_CTRL_1, + pll_code.n_code << RT5682_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5682_PLL_CTRL_2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5682_PLL_M_SFT | + pll_code.m_bp << RT5682_PLL_M_BP_SFT | RT5682_PLL_RST); } - dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", - pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), - pll_code.n_code, pll_code.k_code); + rt5682->pll_in[pll_id] = freq_in; + rt5682->pll_out[pll_id] = freq_out; + rt5682->pll_src[pll_id] = source; - snd_soc_component_write(component, RT5682_PLL_CTRL_1, - pll_code.n_code << RT5682_PLL_N_SFT | pll_code.k_code); - snd_soc_component_write(component, RT5682_PLL_CTRL_2, - (pll_code.m_bp ? 0 : pll_code.m_code) << RT5682_PLL_M_SFT | - pll_code.m_bp << RT5682_PLL_M_BP_SFT | RT5682_PLL_RST); + return 0; +} - rt5682->pll_in = freq_in; - rt5682->pll_out = freq_out; - rt5682->pll_src = source; +static int rt5682_set_bclk1_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->bclk[dai->id] = ratio; + + switch (ratio) { + case 256: + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_256); + break; + case 128: + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_128); + break; + case 64: + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_32); + break; + default: + dev_err(dai->dev, "Invalid bclk1 ratio %d\n", ratio); + return -EINVAL; + } return 0; } -static int rt5682_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +static int rt5682_set_bclk2_ratio(struct snd_soc_dai *dai, unsigned int ratio) { struct snd_soc_component *component = dai->component; struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); @@ -2280,7 +2461,7 @@ static int rt5682_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) RT5682_I2S2_BCLK_MS2_32); break; default: - dev_err(dai->dev, "Invalid bclk ratio %d\n", ratio); + dev_err(dai->dev, "Invalid bclk2 ratio %d\n", ratio); return -EINVAL; } @@ -2319,12 +2500,392 @@ static int rt5682_set_bias_level(struct snd_soc_component *component, return 0; } +#ifdef CONFIG_COMMON_CLK +#define CLK_PLL2_FIN 48000000 +#define CLK_PLL2_FOUT 24576000 +#define CLK_48 48000 + +static bool rt5682_clk_check(struct rt5682_priv *rt5682) +{ + if (!rt5682->master[RT5682_AIF1]) { + dev_err(rt5682->component->dev, "sysclk/dai not set correctly\n"); + return false; + } + return true; +} + +static int rt5682_wclk_prepare(struct clk_hw *hw) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS"); + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, + RT5682_PWR_MB, RT5682_PWR_MB); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "I2S1"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2F"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2B"); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +static void rt5682_wclk_unprepare(struct clk_hw *hw) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + if (!rt5682_clk_check(rt5682)) + return; + + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS"); + if (!rt5682->jack_type) + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, + RT5682_PWR_MB, 0); + snd_soc_dapm_disable_pin_unlocked(dapm, "I2S1"); + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2F"); + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2B"); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); +} + +static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + + if (!rt5682_clk_check(rt5682)) + return 0; + /* + * Only accept to set wclk rate to 48kHz temporarily. + */ + return CLK_48; +} + +static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + /* + * Only accept to set wclk rate to 48kHz temporarily. + */ + return CLK_48; +} + +static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + struct clk *parent_clk; + const char * const clk_name = __clk_get_name(hw->clk); + int pre_div; + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + + /* + * Whether the wclk's parent clk (mclk) exists or not, please ensure + * it is fixed or set to 48MHz before setting wclk rate. It's a + * temporary limitation. Only accept 48MHz clk as the clk provider. + * + * It will set the codec anyway by assuming mclk is 48MHz. + */ + parent_clk = clk_get_parent(hw->clk); + if (!parent_clk) + dev_warn(component->dev, + "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n", + CLK_PLL2_FIN); + + if (parent_rate != CLK_PLL2_FIN) + dev_warn(component->dev, "clk %s only support %d Hz input\n", + clk_name, CLK_PLL2_FIN); + + /* + * It's a temporary limitation. Only accept to set wclk rate to 48kHz. + * It will force wclk to 48kHz even it's not. + */ + if (rate != CLK_48) { + dev_warn(component->dev, "clk %s only support %d Hz output\n", + clk_name, CLK_48); + rate = CLK_48; + } + + /* + * To achieve the rate conversion from 48MHz to 48kHz, PLL2 is needed. + */ + rt5682_set_component_pll(component, RT5682_PLL2, RT5682_PLL2_S_MCLK, + CLK_PLL2_FIN, CLK_PLL2_FOUT); + + rt5682_set_component_sysclk(component, RT5682_SCLK_S_PLL2, 0, + CLK_PLL2_FOUT, SND_SOC_CLOCK_IN); + + pre_div = rl6231_get_clk_info(rt5682->sysclk, rate); + + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1, + RT5682_I2S_M_DIV_MASK | RT5682_I2S_CLK_SRC_MASK, + pre_div << RT5682_I2S_M_DIV_SFT | + (rt5682->sysclk_src) << RT5682_I2S_CLK_SRC_SFT); + + return 0; +} + +static unsigned long rt5682_bclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_BCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + unsigned int bclks_per_wclk; + + snd_soc_component_read(component, RT5682_TDM_TCON_CTRL, + &bclks_per_wclk); + + switch (bclks_per_wclk & RT5682_TDM_BCLK_MS1_MASK) { + case RT5682_TDM_BCLK_MS1_256: + return parent_rate * 256; + case RT5682_TDM_BCLK_MS1_128: + return parent_rate * 128; + case RT5682_TDM_BCLK_MS1_64: + return parent_rate * 64; + case RT5682_TDM_BCLK_MS1_32: + return parent_rate * 32; + default: + return 0; + } +} + +static unsigned long rt5682_bclk_get_factor(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long factor; + + factor = rate / parent_rate; + if (factor < 64) + return 32; + else if (factor < 128) + return 64; + else if (factor < 256) + return 128; + else + return 256; +} + +static long rt5682_bclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_BCLK_IDX]); + unsigned long factor; + + if (!*parent_rate || !rt5682_clk_check(rt5682)) + return -EINVAL; + + /* + * BCLK rates are set as a multiplier of WCLK in HW. + * We don't allow changing the parent WCLK. We just do + * some rounding down based on the parent WCLK rate + * and find the appropriate multiplier of BCLK to + * get the rounded down BCLK value. + */ + factor = rt5682_bclk_get_factor(rate, *parent_rate); + + return *parent_rate * factor; +} + +static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_BCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + struct snd_soc_dai *dai = NULL; + unsigned long factor; + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + + factor = rt5682_bclk_get_factor(rate, parent_rate); + + for_each_component_dais(component, dai) + if (dai->id == RT5682_AIF1) + break; + if (!dai) { + dev_err(component->dev, "dai %d not found in component\n", + RT5682_AIF1); + return -ENODEV; + } + + return rt5682_set_bclk1_ratio(dai, factor); +} + +static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = { + [RT5682_DAI_WCLK_IDX] = { + .prepare = rt5682_wclk_prepare, + .unprepare = rt5682_wclk_unprepare, + .recalc_rate = rt5682_wclk_recalc_rate, + .round_rate = rt5682_wclk_round_rate, + .set_rate = rt5682_wclk_set_rate, + }, + [RT5682_DAI_BCLK_IDX] = { + .recalc_rate = rt5682_bclk_recalc_rate, + .round_rate = rt5682_bclk_round_rate, + .set_rate = rt5682_bclk_set_rate, + }, +}; + +static int rt5682_register_dai_clks(struct snd_soc_component *component) +{ + struct device *dev = component->dev; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct rt5682_platform_data *pdata = &rt5682->pdata; + struct clk_init_data init; + struct clk *dai_clk; + struct clk_lookup *dai_clk_lookup; + struct clk_hw *dai_clk_hw; + const char *parent_name; + int i, ret; + + for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) { + dai_clk_hw = &rt5682->dai_clks_hw[i]; + + switch (i) { + case RT5682_DAI_WCLK_IDX: + /* Make MCLK the parent of WCLK */ + if (rt5682->mclk) { + parent_name = __clk_get_name(rt5682->mclk); + init.parent_names = &parent_name; + init.num_parents = 1; + } else { + init.parent_names = NULL; + init.num_parents = 0; + } + break; + case RT5682_DAI_BCLK_IDX: + /* Make WCLK the parent of BCLK */ + parent_name = __clk_get_name( + rt5682->dai_clks[RT5682_DAI_WCLK_IDX]); + init.parent_names = &parent_name; + init.num_parents = 1; + break; + default: + dev_err(dev, "Invalid clock index\n"); + ret = -EINVAL; + goto err; + } + + init.name = pdata->dai_clk_names[i]; + init.ops = &rt5682_dai_clk_ops[i]; + init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; + dai_clk_hw->init = &init; + + dai_clk = devm_clk_register(dev, dai_clk_hw); + if (IS_ERR(dai_clk)) { + dev_warn(dev, "Failed to register %s: %ld\n", + init.name, PTR_ERR(dai_clk)); + ret = PTR_ERR(dai_clk); + goto err; + } + rt5682->dai_clks[i] = dai_clk; + + if (dev->of_node) { + devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + dai_clk_hw); + } else { + dai_clk_lookup = clkdev_create(dai_clk, init.name, + "%s", dev_name(dev)); + if (!dai_clk_lookup) { + ret = -ENOMEM; + goto err; + } else { + rt5682->dai_clks_lookup[i] = dai_clk_lookup; + } + } + } + + return 0; + +err: + do { + if (rt5682->dai_clks_lookup[i]) + clkdev_drop(rt5682->dai_clks_lookup[i]); + } while (i-- > 0); + + return ret; +} +#endif /* CONFIG_COMMON_CLK */ + static int rt5682_probe(struct snd_soc_component *component) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct sdw_slave *slave; + unsigned long time; +#ifdef CONFIG_COMMON_CLK + int ret; +#endif rt5682->component = component; + if (rt5682->is_sdw) { + slave = rt5682->slave; + time = wait_for_completion_timeout( + &slave->initialization_complete, + msecs_to_jiffies(RT5682_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + } else { +#ifdef CONFIG_COMMON_CLK + /* Check if MCLK provided */ + rt5682->mclk = devm_clk_get(component->dev, "mclk"); + if (IS_ERR(rt5682->mclk)) { + if (PTR_ERR(rt5682->mclk) != -ENOENT) { + ret = PTR_ERR(rt5682->mclk); + return ret; + } + rt5682->mclk = NULL; + } else { + /* Register CCF DAI clock control */ + ret = rt5682_register_dai_clks(component); + if (ret) + return ret; + } + /* Initial setup for CCF */ + rt5682->lrck[RT5682_AIF1] = CLK_48; +#endif + } + return 0; } @@ -2332,7 +2893,16 @@ static void rt5682_remove(struct snd_soc_component *component) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); - rt5682_reset(rt5682->regmap); +#ifdef CONFIG_COMMON_CLK + int i; + + for (i = RT5682_DAI_NUM_CLKS - 1; i >= 0; --i) { + if (rt5682->dai_clks_lookup[i]) + clkdev_drop(rt5682->dai_clks_lookup[i]); + } +#endif + + rt5682_reset(rt5682); } #ifdef CONFIG_PM @@ -2369,14 +2939,203 @@ static const struct snd_soc_dai_ops rt5682_aif1_dai_ops = { .hw_params = rt5682_hw_params, .set_fmt = rt5682_set_dai_fmt, .set_tdm_slot = rt5682_set_tdm_slot, + .set_bclk_ratio = rt5682_set_bclk1_ratio, }; static const struct snd_soc_dai_ops rt5682_aif2_dai_ops = { .hw_params = rt5682_hw_params, .set_fmt = rt5682_set_dai_fmt, - .set_bclk_ratio = rt5682_set_bclk_ratio, + .set_bclk_ratio = rt5682_set_bclk2_ratio, }; +#if IS_ENABLED(CONFIG_SND_SOC_RT5682_SDW) +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt5682_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt5682_sdw_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 rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + unsigned int val_p = 0, val_c = 0, osr_p = 0, osr_c = 0; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -ENOMEM; + + if (!rt5682->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + direction = SDW_DATA_DIR_TX; + port = 2; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.num = port; + + retval = sdw_stream_add_slave(rt5682->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + switch (params_rate(params)) { + case 48000: + val_p = RT5682_SDW_REF_1_48K; + val_c = RT5682_SDW_REF_2_48K; + break; + case 96000: + val_p = RT5682_SDW_REF_1_96K; + val_c = RT5682_SDW_REF_2_96K; + break; + case 192000: + val_p = RT5682_SDW_REF_1_192K; + val_c = RT5682_SDW_REF_2_192K; + break; + case 32000: + val_p = RT5682_SDW_REF_1_32K; + val_c = RT5682_SDW_REF_2_32K; + break; + case 24000: + val_p = RT5682_SDW_REF_1_24K; + val_c = RT5682_SDW_REF_2_24K; + break; + case 16000: + val_p = RT5682_SDW_REF_1_16K; + val_c = RT5682_SDW_REF_2_16K; + break; + case 12000: + val_p = RT5682_SDW_REF_1_12K; + val_c = RT5682_SDW_REF_2_12K; + break; + case 8000: + val_p = RT5682_SDW_REF_1_8K; + val_c = RT5682_SDW_REF_2_8K; + break; + case 44100: + val_p = RT5682_SDW_REF_1_44K; + val_c = RT5682_SDW_REF_2_44K; + break; + case 88200: + val_p = RT5682_SDW_REF_1_88K; + val_c = RT5682_SDW_REF_2_88K; + break; + case 176400: + val_p = RT5682_SDW_REF_1_176K; + val_c = RT5682_SDW_REF_2_176K; + break; + case 22050: + val_p = RT5682_SDW_REF_1_22K; + val_c = RT5682_SDW_REF_2_22K; + break; + case 11025: + val_p = RT5682_SDW_REF_1_11K; + val_c = RT5682_SDW_REF_2_11K; + break; + default: + return -EINVAL; + } + + if (params_rate(params) <= 48000) { + osr_p = RT5682_DAC_OSR_D_8; + osr_c = RT5682_ADC_OSR_D_8; + } else if (params_rate(params) <= 96000) { + osr_p = RT5682_DAC_OSR_D_4; + osr_c = RT5682_ADC_OSR_D_4; + } else { + osr_p = RT5682_DAC_OSR_D_2; + osr_c = RT5682_ADC_OSR_D_2; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK, + RT5682_SDW_REF_1_MASK, val_p); + regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1, + RT5682_DAC_OSR_MASK, osr_p); + } else { + regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK, + RT5682_SDW_REF_2_MASK, val_c); + regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1, + RT5682_ADC_OSR_MASK, osr_c); + } + + return retval; +} + +static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt5682->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt5682->slave, stream->sdw_stream); + return 0; +} + +static 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, + .shutdown = rt5682_sdw_shutdown, +}; +#endif + static struct snd_soc_dai_driver rt5682_dai[] = { { .name = "rt5682-aif1", @@ -2409,6 +3168,27 @@ static struct snd_soc_dai_driver rt5682_dai[] = { }, .ops = &rt5682_aif2_dai_ops, }, +#if IS_ENABLED(CONFIG_SND_SOC_RT5682_SDW) + { + .name = "rt5682-sdw", + .id = RT5682_SDW, + .playback = { + .stream_name = "SDW Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .capture = { + .stream_name = "SDW Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_sdw_ops, + }, +#endif }; static const struct snd_soc_component_driver soc_component_dev_rt5682 = { @@ -2461,10 +3241,21 @@ static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) &rt5682->pdata.jd_src); device_property_read_u32(dev, "realtek,btndet-delay", &rt5682->pdata.btndet_delay); + device_property_read_u32(dev, "realtek,dmic-clk-rate-hz", + &rt5682->pdata.dmic_clk_rate); + device_property_read_u32(dev, "realtek,dmic-delay-ms", + &rt5682->pdata.dmic_delay); rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node, "realtek,ldo1-en-gpios", 0); + if (device_property_read_string_array(dev, "clock-output-names", + rt5682->pdata.dai_clk_names, + RT5682_DAI_NUM_CLKS) < 0) + dev_warn(dev, "Using default DAI clk names: %s, %s\n", + rt5682->pdata.dai_clk_names[RT5682_DAI_WCLK_IDX], + rt5682->pdata.dai_clk_names[RT5682_DAI_BCLK_IDX]); + return 0; } @@ -2474,7 +3265,7 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) mutex_lock(&rt5682->calibrate_mutex); - rt5682_reset(rt5682->regmap); + rt5682_reset(rt5682); regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af); usleep_range(15000, 20000); @@ -2520,6 +3311,221 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) } +#if IS_ENABLED(CONFIG_SND_SOC_RT5682_SDW) +static int rt5682_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + unsigned int data_l, data_h; + + regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 0); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff)); + regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_H, &data_h); + regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_L, &data_l); + + *val = (data_h << 8) | data_l; + + dev_vdbg(dev, "[%s] %04x => %04x\n", __func__, reg, *val); + + return 0; +} + +static int rt5682_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + + regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 1); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff)); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_H, (val >> 8) & 0xff); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_L, (val & 0xff)); + + dev_vdbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + + return 0; +} + +static const struct regmap_config rt5682_sdw_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5682_I2C_MODE, + .volatile_reg = rt5682_volatile_register, + .readable_reg = rt5682_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5682_reg, + .num_reg_defaults = ARRAY_SIZE(rt5682_reg), + .use_single_read = true, + .use_single_write = true, + .reg_read = rt5682_sdw_read, + .reg_write = rt5682_sdw_write, +}; + +int rt5682_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt5682_priv *rt5682; + int ret; + + rt5682 = devm_kzalloc(dev, sizeof(*rt5682), GFP_KERNEL); + if (!rt5682) + return -ENOMEM; + + dev_set_drvdata(dev, rt5682); + rt5682->slave = slave; + rt5682->sdw_regmap = regmap; + rt5682->is_sdw = true; + + rt5682->regmap = devm_regmap_init(dev, NULL, dev, &rt5682_sdw_regmap); + if (IS_ERR(rt5682->regmap)) { + ret = PTR_ERR(rt5682->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt5682->hw_init = false; + rt5682->first_hw_init = false; + + mutex_init(&rt5682->calibrate_mutex); + INIT_DELAYED_WORK(&rt5682->jack_detect_work, + rt5682_jack_detect_handler); + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_rt5682, + rt5682_dai, ARRAY_SIZE(rt5682_dai)); + + dev_dbg(&slave->dev, "%s\n", __func__); + + return ret; +} +EXPORT_SYMBOL_GPL(rt5682_sdw_init); + +int rt5682_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + int ret = 0; + unsigned int val; + + if (rt5682->hw_init) + return 0; + + regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val); + if (val != DEVICE_ID) { + pr_err("Device with ID register %x is not rt5682\n", val); + return -ENODEV; + } + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt5682->first_hw_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt5682_reset(rt5682); + + if (rt5682->first_hw_init) { + regcache_cache_only(rt5682->regmap, false); + regcache_cache_bypass(rt5682->regmap, true); + } + + rt5682_calibrate(rt5682); + + if (rt5682->first_hw_init) { + regcache_cache_bypass(rt5682->regmap, false); + regcache_mark_dirty(rt5682->regmap); + regcache_sync(rt5682->regmap); + + /* volatile registers */ + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, + RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); + + goto reinit; + } + + ret = regmap_multi_reg_write(rt5682->regmap, patch_list, + ARRAY_SIZE(patch_list)); + if (ret != 0) + dev_warn(dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000); + + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK, + RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8, + RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA); + regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1, + RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ); + regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, + RT5682_PM_HP_MASK, RT5682_PM_HP_HV); + + /* Soundwire */ + regmap_write(rt5682->regmap, RT5682_PLL2_INTERNAL, 0xa266); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_1, 0x1700); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_2, 0x0006); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_3, 0x2600); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_4, 0x0c8f); + regmap_write(rt5682->regmap, RT5682_PLL_TRACK_2, 0x3000); + regmap_write(rt5682->regmap, RT5682_PLL_TRACK_3, 0x4000); + regmap_update_bits(rt5682->regmap, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK | RT5682_PLL2_SRC_MASK, + RT5682_SCLK_SRC_PLL2 | RT5682_PLL2_SRC_SDW); + + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, + RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); + regmap_write(rt5682->regmap, RT5682_CBJ_CTRL_1, 0xd042); + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_3, + RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN); + regmap_update_bits(rt5682->regmap, RT5682_SAR_IL_CMD_1, + RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_IRQ | RT5682_POW_JDH | + RT5682_POW_ANA, RT5682_POW_IRQ | + RT5682_POW_JDH | RT5682_POW_ANA); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, + RT5682_PWR_JDH, RT5682_PWR_JDH); + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK | RT5682_JD1_IRQ_MASK, + RT5682_JD1_EN | RT5682_JD1_IRQ_PUL); + +reinit: + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + + /* Mark Slave initialization complete */ + rt5682->hw_init = true; + rt5682->first_hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + + return ret; +} +EXPORT_SYMBOL_GPL(rt5682_io_init); +#endif + static int rt5682_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -2586,7 +3592,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - rt5682_reset(rt5682->regmap); + rt5682_reset(rt5682); mutex_init(&rt5682->calibrate_mutex); rt5682_calibrate(rt5682); @@ -2651,6 +3657,8 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ); regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_HV); + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_FIFO_CLK_DIV_MASK, RT5682_FIFO_CLK_DIV_2); INIT_DELAYED_WORK(&rt5682->jack_detect_work, rt5682_jack_detect_handler); @@ -2676,7 +3684,7 @@ static void rt5682_i2c_shutdown(struct i2c_client *client) { struct rt5682_priv *rt5682 = i2c_get_clientdata(client); - rt5682_reset(rt5682->regmap); + rt5682_reset(rt5682); } #ifdef CONFIG_OF diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 18faaa2a49a0..0baeece84ec4 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -10,6 +10,12 @@ #define __RT5682_H__ #include <sound/rt5682.h> +#include <linux/regulator/consumer.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> #define DEVICE_ID 0x6530 @@ -177,7 +183,7 @@ #define RT5682_TEST_MODE_CTRL_4 0x0148 #define RT5682_TEST_MODE_CTRL_5 0x0149 #define RT5682_PLL1_INTERNAL 0x0150 -#define RT5682_PLL2_INTERNAL 0x0151 +#define RT5682_PLL2_INTERNAL 0x0156 #define RT5682_STO_NG2_CTRL_1 0x0160 #define RT5682_STO_NG2_CTRL_2 0x0161 #define RT5682_STO_NG2_CTRL_3 0x0162 @@ -651,6 +657,8 @@ #define RT5682_DMIC_1_EN_SFT 15 #define RT5682_DMIC_1_DIS (0x0 << 15) #define RT5682_DMIC_1_EN (0x1 << 15) +#define RT5682_FIFO_CLK_DIV_MASK (0x7 << 12) +#define RT5682_FIFO_CLK_DIV_2 (0x1 << 12) #define RT5682_DMIC_1_DP_MASK (0x3 << 4) #define RT5682_DMIC_1_DP_SFT 4 #define RT5682_DMIC_1_DP_GPIO2 (0x0 << 4) @@ -738,7 +746,7 @@ #define RT5682_ADC_OSR_D_24 (0x7 << 12) #define RT5682_ADC_OSR_D_32 (0x8 << 12) #define RT5682_ADC_OSR_D_48 (0x9 << 12) -#define RT5682_I2S_M_DIV_MASK (0xf << 12) +#define RT5682_I2S_M_DIV_MASK (0xf << 8) #define RT5682_I2S_M_DIV_SFT 8 #define RT5682_I2S_M_D_1 (0x0 << 8) #define RT5682_I2S_M_D_2 (0x1 << 8) @@ -820,6 +828,12 @@ #define RT5682_TDM_DF_PCM_B (0x3 << 11) #define RT5682_TDM_DF_PCM_A_N (0x6 << 11) #define RT5682_TDM_DF_PCM_B_N (0x7 << 11) +#define RT5682_TDM_BCLK_MS1_MASK (0x3 << 9) +#define RT5682_TDM_BCLK_MS1_SFT 9 +#define RT5682_TDM_BCLK_MS1_32 (0x0 << 9) +#define RT5682_TDM_BCLK_MS1_64 (0x1 << 9) +#define RT5682_TDM_BCLK_MS1_128 (0x2 << 9) +#define RT5682_TDM_BCLK_MS1_256 (0x3 << 9) #define RT5682_TDM_CL_MASK (0x3 << 4) #define RT5682_TDM_CL_16 (0x0 << 4) #define RT5682_TDM_CL_20 (0x1 << 4) @@ -835,8 +849,8 @@ #define RT5682_TDM_M_LP_INV (0x1 << 1) #define RT5682_TDM_MS_MASK (0x1 << 0) #define RT5682_TDM_MS_SFT 0 -#define RT5682_TDM_MS_M (0x0 << 0) -#define RT5682_TDM_MS_S (0x1 << 0) +#define RT5682_TDM_MS_S (0x0 << 0) +#define RT5682_TDM_MS_M (0x1 << 0) /* Global Clock Control (0x0080) */ #define RT5682_SCLK_SRC_MASK (0x7 << 13) @@ -1049,6 +1063,28 @@ #define RT5682_PWR_CLK1M_PD (0x0 << 8) #define RT5682_PWR_CLK1M_PU (0x1 << 8) +/* PLL2 M/N/K Code Control 1 (0x009b) */ +#define RT5682_PLL2F_K_MASK (0x1f << 8) +#define RT5682_PLL2F_K_SFT 8 +#define RT5682_PLL2B_K_MASK (0xf << 4) +#define RT5682_PLL2B_K_SFT 4 +#define RT5682_PLL2B_M_MASK (0xf << 0) + +/* PLL2 M/N/K Code Control 2 (0x009c) */ +#define RT5682_PLL2F_M_MASK (0x3f << 8) +#define RT5682_PLL2F_M_SFT 8 +#define RT5682_PLL2B_N_MASK (0x3f << 0) + +/* PLL2 M/N/K Code Control 2 (0x009d) */ +#define RT5682_PLL2F_N_MASK (0x7f << 8) +#define RT5682_PLL2F_N_SFT 8 + +/* PLL2 M/N/K Code Control 2 (0x009e) */ +#define RT5682_PLL2B_M_BP_MASK (0x1 << 11) +#define RT5682_PLL2B_M_BP_SFT 11 +#define RT5682_PLL2F_M_BP_MASK (0x1 << 7) +#define RT5682_PLL2F_M_BP_SFT 7 + /* RC Clock Control (0x009f) */ #define RT5682_POW_IRQ (0x1 << 15) #define RT5682_POW_JDH (0x1 << 14) @@ -1091,11 +1127,17 @@ #define RT5682_JD1_POL_MASK (0x1 << 13) #define RT5682_JD1_POL_NOR (0x0 << 13) #define RT5682_JD1_POL_INV (0x1 << 13) +#define RT5682_JD1_IRQ_MASK (0x1 << 10) +#define RT5682_JD1_IRQ_LEV (0x0 << 10) +#define RT5682_JD1_IRQ_PUL (0x1 << 10) /* IRQ Control 3 (0x00b8) */ #define RT5682_IL_IRQ_MASK (0x1 << 7) #define RT5682_IL_IRQ_DIS (0x0 << 7) #define RT5682_IL_IRQ_EN (0x1 << 7) +#define RT5682_IL_IRQ_TYPE_MASK (0x1 << 4) +#define RT5682_IL_IRQ_LEV (0x0 << 4) +#define RT5682_IL_IRQ_PUL (0x1 << 4) /* GPIO Control 1 (0x00c0) */ #define RT5682_GP1_PIN_MASK (0x3 << 14) @@ -1309,11 +1351,19 @@ enum { RT5682_PLL1_S_MCLK, RT5682_PLL1_S_BCLK1, RT5682_PLL1_S_RCCLK, + RT5682_PLL2_S_MCLK, +}; + +enum { + RT5682_PLL1, + RT5682_PLL2, + RT5682_PLLS, }; enum { RT5682_AIF1, RT5682_AIF2, + RT5682_SDW, RT5682_AIFS }; @@ -1329,7 +1379,49 @@ enum { RT5682_CLK_SEL_I2S2_ASRC, }; +#define RT5682_NUM_SUPPLIES 3 + +struct rt5682_priv { + struct snd_soc_component *component; + struct rt5682_platform_data pdata; + struct regmap *regmap; + struct regmap *sdw_regmap; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + struct sdw_slave *slave; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + bool is_sdw; + +#ifdef CONFIG_COMMON_CLK + struct clk_hw dai_clks_hw[RT5682_DAI_NUM_CLKS]; + struct clk_lookup *dai_clks_lookup[RT5682_DAI_NUM_CLKS]; + struct clk *dai_clks[RT5682_DAI_NUM_CLKS]; + struct clk *mclk; +#endif + + int sysclk; + int sysclk_src; + int lrck[RT5682_AIFS]; + int bclk[RT5682_AIFS]; + int master[RT5682_AIFS]; + + int pll_src[RT5682_PLLS]; + int pll_in[RT5682_PLLS]; + int pll_out[RT5682_PLLS]; + + int jack_type; +}; + int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, unsigned int filter_mask, unsigned int clk_src); +int rt5682_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave); +int rt5682_io_init(struct device *dev, struct sdw_slave *slave); #endif /* __RT5682_H__ */ diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index be52886a5edb..7fae88655a0f 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -26,6 +26,24 @@ #define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FORMAT_S32_LE) +/* DVC equation involves floating point math + * round(10^(volume in dB/20)*2^30) + * so create a lookup table for 2dB step + */ +static const unsigned int float_vol_db_lookup[] = { +0x00000d43, 0x000010b2, 0x00001505, 0x00001a67, 0x00002151, +0x000029f1, 0x000034cd, 0x00004279, 0x000053af, 0x0000695b, +0x0000695b, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, +0x0001a36e, 0x00021008, 0x000298c0, 0x000344df, 0x00041d8f, +0x00052e5a, 0x000685c8, 0x00083621, 0x000a566d, 0x000d03a7, +0x0010624d, 0x0014a050, 0x0019f786, 0x0020b0bc, 0x0029279d, +0x0033cf8d, 0x004139d3, 0x00521d50, 0x00676044, 0x0082248a, +0x00a3d70a, 0x00ce4328, 0x0103ab3d, 0x0146e75d, 0x019b8c27, +0x02061b89, 0x028c423f, 0x03352529, 0x0409c2b0, 0x05156d68, +0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, 0x143d1362, +0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000 +}; + struct tas2562_data { struct snd_soc_component *component; struct gpio_desc *sdz_gpio; @@ -34,6 +52,12 @@ struct tas2562_data { struct i2c_client *client; int v_sense_slot; int i_sense_slot; + int volume_lvl; +}; + +enum tas256x_model { + TAS2562, + TAS2563, }; static int tas2562_set_bias_level(struct snd_soc_component *component, @@ -383,21 +407,81 @@ static int tas2562_dac_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int ret; switch (event) { case SND_SOC_DAPM_POST_PMU: - dev_info(tas2562->dev, "SND_SOC_DAPM_POST_PMU\n"); + ret = snd_soc_component_update_bits(component, + TAS2562_PWR_CTRL, + TAS2562_MODE_MASK, + TAS2562_MUTE); + if (ret) + goto end; break; case SND_SOC_DAPM_PRE_PMD: - dev_info(tas2562->dev, "SND_SOC_DAPM_PRE_PMD\n"); + ret = snd_soc_component_update_bits(component, + TAS2562_PWR_CTRL, + TAS2562_MODE_MASK, + TAS2562_SHUTDOWN); + if (ret) + goto end; break; default: - break; + dev_err(tas2562->dev, "Not supported evevt\n"); + return -EINVAL; } +end: + if (ret < 0) + return ret; + + return 0; +} + +static int tas2562_volume_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = tas2562->volume_lvl; return 0; } +static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int ret; + u32 reg_val; + + reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, + (reg_val & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, + ((reg_val >> 8) & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, + ((reg_val >> 16) & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, + ((reg_val >> 24) & 0xff)); + if (ret) + return ret; + + tas2562->volume_lvl = ucontrol->value.integer.value[0]; + + return ret; +} + +/* Digital Volume Control. From 0 dB to -110 dB in 1 dB steps */ +static const DECLARE_TLV_DB_SCALE(dvc_tlv, -11000, 100, 0); + static DECLARE_TLV_DB_SCALE(tas2562_dac_tlv, 850, 50, 0); static const struct snd_kcontrol_new isense_switch = @@ -409,14 +493,24 @@ static const struct snd_kcontrol_new vsense_switch = 1, 1); static const struct snd_kcontrol_new tas2562_snd_controls[] = { - SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 0, 0x1c, 0, + SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 1, 0x1c, 0, tas2562_dac_tlv), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Volume Control", + .index = 0, + .tlv.p = dvc_tlv, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = tas2562_volume_control_get, + .put = tas2562_volume_control_put, + .private_value = SOC_SINGLE_VALUE(TAS2562_DVC_CFG1, 0, 110, 0, 0) , + }, }; static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux), - SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SWITCH("ISENSE", TAS2562_PWR_CTRL, 3, 1, &isense_switch), @@ -431,7 +525,7 @@ static const struct snd_soc_dapm_route tas2562_audio_map[] = { {"ASI1 Sel", "Left", "ASI1"}, {"ASI1 Sel", "Right", "ASI1"}, {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, - { "DAC", NULL, "DAC IN" }, + { "DAC", NULL, "ASI1 Sel" }, { "OUT", NULL, "DAC" }, {"ISENSE", "Switch", "IMON"}, {"VSENSE", "Switch", "VMON"}, @@ -472,6 +566,13 @@ static struct snd_soc_dai_driver tas2562_dai[] = { .rates = SNDRV_PCM_RATE_8000_192000, .formats = TAS2562_FORMATS, }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 0, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2562_FORMATS, + }, .ops = &tas2562_speaker_dai_ops, }, }; @@ -495,6 +596,10 @@ static const struct reg_default tas2562_reg_defaults[] = { { TAS2562_PB_CFG1, 0x20 }, { TAS2562_TDM_CFG0, 0x09 }, { TAS2562_TDM_CFG1, 0x02 }, + { TAS2562_DVC_CFG1, 0x40 }, + { TAS2562_DVC_CFG2, 0x40 }, + { TAS2562_DVC_CFG3, 0x00 }, + { TAS2562_DVC_CFG4, 0x00 }, }; static const struct regmap_config tas2562_regmap_config = { @@ -564,13 +669,15 @@ static int tas2562_probe(struct i2c_client *client, } static const struct i2c_device_id tas2562_id[] = { - { "tas2562", 0 }, + { "tas2562", TAS2562 }, + { "tas2563", TAS2563 }, { } }; MODULE_DEVICE_TABLE(i2c, tas2562_id); static const struct of_device_id tas2562_of_match[] = { { .compatible = "ti,tas2562", }, + { .compatible = "ti,tas2563", }, { }, }; MODULE_DEVICE_TABLE(of, tas2562_of_match); diff --git a/sound/soc/codecs/tas2562.h b/sound/soc/codecs/tas2562.h index 62e659ab786d..28e75fc431d0 100644 --- a/sound/soc/codecs/tas2562.h +++ b/sound/soc/codecs/tas2562.h @@ -35,12 +35,14 @@ #define TAS2562_REV_ID TAS2562_REG(0, 0x7d) /* Page 2 */ -#define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x01) -#define TAS2562_DVC_CFG2 TAS2562_REG(2, 0x02) +#define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x0c) +#define TAS2562_DVC_CFG2 TAS2562_REG(2, 0x0d) +#define TAS2562_DVC_CFG3 TAS2562_REG(2, 0x0e) +#define TAS2562_DVC_CFG4 TAS2562_REG(2, 0x0f) #define TAS2562_RESET BIT(0) -#define TAS2562_MODE_MASK 0x3 +#define TAS2562_MODE_MASK GENMASK(1,0) #define TAS2562_ACTIVE 0x0 #define TAS2562_MUTE 0x1 #define TAS2562_SHUTDOWN 0x2 @@ -73,8 +75,8 @@ #define TAS2562_TDM_CFG2_RXWLEN_24B BIT(3) #define TAS2562_TDM_CFG2_RXWLEN_32B (BIT(2) | BIT(3)) -#define TAS2562_VSENSE_POWER_EN BIT(2) -#define TAS2562_ISENSE_POWER_EN BIT(3) +#define TAS2562_VSENSE_POWER_EN 2 +#define TAS2562_ISENSE_POWER_EN 3 #define TAS2562_TDM_CFG5_VSNS_EN BIT(6) #define TAS2562_TDM_CFG5_VSNS_SLOT_MASK GENMASK(5, 0) diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c new file mode 100644 index 000000000000..38897568ee96 --- /dev/null +++ b/sound/soc/codecs/tlv320adcx140.c @@ -0,0 +1,920 @@ +// SPDX-License-Identifier: GPL-2.0 +// TLV320ADCX140 Sound driver +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> +#include <linux/acpi.h> +#include <linux/of.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/initval.h> +#include <sound/tlv.h> + +#include "tlv320adcx140.h" + +struct adcx140_priv { + struct snd_soc_component *component; + struct regulator *supply_areg; + struct gpio_desc *gpio_reset; + struct regmap *regmap; + struct device *dev; + + int micbias_vg; + + unsigned int dai_fmt; + unsigned int tdm_delay; + unsigned int slot_width; +}; + +static const struct reg_default adcx140_reg_defaults[] = { + { ADCX140_PAGE_SELECT, 0x00 }, + { ADCX140_SW_RESET, 0x00 }, + { ADCX140_SLEEP_CFG, 0x00 }, + { ADCX140_SHDN_CFG, 0x05 }, + { ADCX140_ASI_CFG0, 0x30 }, + { ADCX140_ASI_CFG1, 0x00 }, + { ADCX140_ASI_CFG2, 0x00 }, + { ADCX140_ASI_CH1, 0x00 }, + { ADCX140_ASI_CH2, 0x01 }, + { ADCX140_ASI_CH3, 0x02 }, + { ADCX140_ASI_CH4, 0x03 }, + { ADCX140_ASI_CH5, 0x04 }, + { ADCX140_ASI_CH6, 0x05 }, + { ADCX140_ASI_CH7, 0x06 }, + { ADCX140_ASI_CH8, 0x07 }, + { ADCX140_MST_CFG0, 0x02 }, + { ADCX140_MST_CFG1, 0x48 }, + { ADCX140_ASI_STS, 0xff }, + { ADCX140_CLK_SRC, 0x10 }, + { ADCX140_PDMCLK_CFG, 0x40 }, + { ADCX140_PDM_CFG, 0x00 }, + { ADCX140_GPIO_CFG0, 0x22 }, + { ADCX140_GPO_CFG1, 0x00 }, + { ADCX140_GPO_CFG2, 0x00 }, + { ADCX140_GPO_CFG3, 0x00 }, + { ADCX140_GPO_CFG4, 0x00 }, + { ADCX140_GPO_VAL, 0x00 }, + { ADCX140_GPIO_MON, 0x00 }, + { ADCX140_GPI_CFG0, 0x00 }, + { ADCX140_GPI_CFG1, 0x00 }, + { ADCX140_GPI_MON, 0x00 }, + { ADCX140_INT_CFG, 0x00 }, + { ADCX140_INT_MASK0, 0xff }, + { ADCX140_INT_LTCH0, 0x00 }, + { ADCX140_BIAS_CFG, 0x00 }, + { ADCX140_CH1_CFG0, 0x00 }, + { ADCX140_CH1_CFG1, 0x00 }, + { ADCX140_CH1_CFG2, 0xc9 }, + { ADCX140_CH1_CFG3, 0x80 }, + { ADCX140_CH1_CFG4, 0x00 }, + { ADCX140_CH2_CFG0, 0x00 }, + { ADCX140_CH2_CFG1, 0x00 }, + { ADCX140_CH2_CFG2, 0xc9 }, + { ADCX140_CH2_CFG3, 0x80 }, + { ADCX140_CH2_CFG4, 0x00 }, + { ADCX140_CH3_CFG0, 0x00 }, + { ADCX140_CH3_CFG1, 0x00 }, + { ADCX140_CH3_CFG2, 0xc9 }, + { ADCX140_CH3_CFG3, 0x80 }, + { ADCX140_CH3_CFG4, 0x00 }, + { ADCX140_CH4_CFG0, 0x00 }, + { ADCX140_CH4_CFG1, 0x00 }, + { ADCX140_CH4_CFG2, 0xc9 }, + { ADCX140_CH4_CFG3, 0x80 }, + { ADCX140_CH4_CFG4, 0x00 }, + { ADCX140_CH5_CFG2, 0xc9 }, + { ADCX140_CH5_CFG3, 0x80 }, + { ADCX140_CH5_CFG4, 0x00 }, + { ADCX140_CH6_CFG2, 0xc9 }, + { ADCX140_CH6_CFG3, 0x80 }, + { ADCX140_CH6_CFG4, 0x00 }, + { ADCX140_CH7_CFG2, 0xc9 }, + { ADCX140_CH7_CFG3, 0x80 }, + { ADCX140_CH7_CFG4, 0x00 }, + { ADCX140_CH8_CFG2, 0xc9 }, + { ADCX140_CH8_CFG3, 0x80 }, + { ADCX140_CH8_CFG4, 0x00 }, + { ADCX140_DSP_CFG0, 0x01 }, + { ADCX140_DSP_CFG1, 0x40 }, + { ADCX140_DRE_CFG0, 0x7b }, + { ADCX140_AGC_CFG0, 0xe7 }, + { ADCX140_IN_CH_EN, 0xf0 }, + { ADCX140_ASI_OUT_CH_EN, 0x00 }, + { ADCX140_PWR_CFG, 0x00 }, + { ADCX140_DEV_STS0, 0x00 }, + { ADCX140_DEV_STS1, 0x80 }, +}; + +static const struct regmap_range_cfg adcx140_ranges[] = { + { + .range_min = 0, + .range_max = 12 * 128, + .selector_reg = ADCX140_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static bool adcx140_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADCX140_SW_RESET: + case ADCX140_DEV_STS0: + case ADCX140_DEV_STS1: + case ADCX140_ASI_STS: + return true; + default: + return false; + } +} + +static const struct regmap_config adcx140_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .reg_defaults = adcx140_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adcx140_reg_defaults), + .cache_type = REGCACHE_FLAT, + .ranges = adcx140_ranges, + .num_ranges = ARRAY_SIZE(adcx140_ranges), + .max_register = 12 * 128, + .volatile_reg = adcx140_volatile, +}; + +/* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */ +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10000, 50, 0); + +/* ADC gain. From 0 to 42 dB in 1 dB steps */ +static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); + +/* DRE Level. From -12 dB to -66 dB in 1 dB steps */ +static DECLARE_TLV_DB_SCALE(dre_thresh_tlv, -6600, 100, 0); +/* DRE Max Gain. From 2 dB to 26 dB in 2 dB steps */ +static DECLARE_TLV_DB_SCALE(dre_gain_tlv, 200, 200, 0); + +/* AGC Level. From -6 dB to -36 dB in 2 dB steps */ +static DECLARE_TLV_DB_SCALE(agc_thresh_tlv, -3600, 200, 0); +/* AGC Max Gain. From 3 dB to 42 dB in 3 dB steps */ +static DECLARE_TLV_DB_SCALE(agc_gain_tlv, 300, 300, 0); + +static const char * const decimation_filter_text[] = { + "Linear Phase", "Low Latency", "Ultra-low Latency" +}; + +static SOC_ENUM_SINGLE_DECL(decimation_filter_enum, ADCX140_DSP_CFG0, 4, + decimation_filter_text); + +static const struct snd_kcontrol_new decimation_filter_controls[] = { + SOC_DAPM_ENUM("Decimation Filter", decimation_filter_enum), +}; + +static const char * const resistor_text[] = { + "2.5 kOhm", "10 kOhm", "20 kOhm" +}; + +static SOC_ENUM_SINGLE_DECL(in1_resistor_enum, ADCX140_CH1_CFG0, 2, + resistor_text); +static SOC_ENUM_SINGLE_DECL(in2_resistor_enum, ADCX140_CH2_CFG0, 2, + resistor_text); +static SOC_ENUM_SINGLE_DECL(in3_resistor_enum, ADCX140_CH3_CFG0, 2, + resistor_text); +static SOC_ENUM_SINGLE_DECL(in4_resistor_enum, ADCX140_CH4_CFG0, 2, + resistor_text); + +static const struct snd_kcontrol_new in1_resistor_controls[] = { + SOC_DAPM_ENUM("CH1 Resistor Select", in1_resistor_enum), +}; +static const struct snd_kcontrol_new in2_resistor_controls[] = { + SOC_DAPM_ENUM("CH2 Resistor Select", in2_resistor_enum), +}; +static const struct snd_kcontrol_new in3_resistor_controls[] = { + SOC_DAPM_ENUM("CH3 Resistor Select", in3_resistor_enum), +}; +static const struct snd_kcontrol_new in4_resistor_controls[] = { + SOC_DAPM_ENUM("CH4 Resistor Select", in4_resistor_enum), +}; + +/* Analog/Digital Selection */ +static const char *adcx140_mic_sel_text[] = {"Analog", "Line In", "Digital"}; +static const char *adcx140_analog_sel_text[] = {"Analog", "Line In"}; + +static SOC_ENUM_SINGLE_DECL(adcx140_mic1p_enum, + ADCX140_CH1_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic1p_control = +SOC_DAPM_ENUM("MIC1P MUX", adcx140_mic1p_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic1_analog_enum, + ADCX140_CH1_CFG0, 7, + adcx140_analog_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic1_analog_control = +SOC_DAPM_ENUM("MIC1 Analog MUX", adcx140_mic1_analog_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic1m_enum, + ADCX140_CH1_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic1m_control = +SOC_DAPM_ENUM("MIC1M MUX", adcx140_mic1m_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic2p_enum, + ADCX140_CH2_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic2p_control = +SOC_DAPM_ENUM("MIC2P MUX", adcx140_mic2p_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic2_analog_enum, + ADCX140_CH2_CFG0, 7, + adcx140_analog_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic2_analog_control = +SOC_DAPM_ENUM("MIC2 Analog MUX", adcx140_mic2_analog_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic2m_enum, + ADCX140_CH2_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic2m_control = +SOC_DAPM_ENUM("MIC2M MUX", adcx140_mic2m_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic3p_enum, + ADCX140_CH3_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic3p_control = +SOC_DAPM_ENUM("MIC3P MUX", adcx140_mic3p_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic3_analog_enum, + ADCX140_CH3_CFG0, 7, + adcx140_analog_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic3_analog_control = +SOC_DAPM_ENUM("MIC3 Analog MUX", adcx140_mic3_analog_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic3m_enum, + ADCX140_CH3_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic3m_control = +SOC_DAPM_ENUM("MIC3M MUX", adcx140_mic3m_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic4p_enum, + ADCX140_CH4_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic4p_control = +SOC_DAPM_ENUM("MIC4P MUX", adcx140_mic4p_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic4_analog_enum, + ADCX140_CH4_CFG0, 7, + adcx140_analog_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic4_analog_control = +SOC_DAPM_ENUM("MIC4 Analog MUX", adcx140_mic4_analog_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic4m_enum, + ADCX140_CH4_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic4m_control = +SOC_DAPM_ENUM("MIC4M MUX", adcx140_mic4m_enum); + +static const struct snd_kcontrol_new adcx140_dapm_ch1_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 7, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch2_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 6, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch3_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 5, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch4_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 4, 1, 0); + +static const struct snd_kcontrol_new adcx140_dapm_ch1_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_CH1_CFG0, 0, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch2_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_CH2_CFG0, 0, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch3_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_CH3_CFG0, 0, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch4_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_CH4_CFG0, 0, 1, 0); + +static const struct snd_kcontrol_new adcx140_dapm_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_DSP_CFG1, 3, 1, 0); + +/* Output Mixer */ +static const struct snd_kcontrol_new adcx140_output_mixer_controls[] = { + SOC_DAPM_SINGLE("Digital CH1 Switch", 0, 0, 0, 0), + SOC_DAPM_SINGLE("Digital CH2 Switch", 0, 0, 0, 0), + SOC_DAPM_SINGLE("Digital CH3 Switch", 0, 0, 0, 0), + SOC_DAPM_SINGLE("Digital CH4 Switch", 0, 0, 0, 0), +}; + +static const struct snd_soc_dapm_widget adcx140_dapm_widgets[] = { + /* Analog Differential Inputs */ + SND_SOC_DAPM_INPUT("MIC1P"), + SND_SOC_DAPM_INPUT("MIC1M"), + SND_SOC_DAPM_INPUT("MIC2P"), + SND_SOC_DAPM_INPUT("MIC2M"), + SND_SOC_DAPM_INPUT("MIC3P"), + SND_SOC_DAPM_INPUT("MIC3M"), + SND_SOC_DAPM_INPUT("MIC4P"), + SND_SOC_DAPM_INPUT("MIC4M"), + + SND_SOC_DAPM_OUTPUT("CH1_OUT"), + SND_SOC_DAPM_OUTPUT("CH2_OUT"), + SND_SOC_DAPM_OUTPUT("CH3_OUT"), + SND_SOC_DAPM_OUTPUT("CH4_OUT"), + SND_SOC_DAPM_OUTPUT("CH5_OUT"), + SND_SOC_DAPM_OUTPUT("CH6_OUT"), + SND_SOC_DAPM_OUTPUT("CH7_OUT"), + SND_SOC_DAPM_OUTPUT("CH8_OUT"), + + SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, + &adcx140_output_mixer_controls[0], + ARRAY_SIZE(adcx140_output_mixer_controls)), + + /* Input Selection to MIC_PGA */ + SND_SOC_DAPM_MUX("MIC1P Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic1p_control), + SND_SOC_DAPM_MUX("MIC2P Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic2p_control), + SND_SOC_DAPM_MUX("MIC3P Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic3p_control), + SND_SOC_DAPM_MUX("MIC4P Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic4p_control), + + /* Input Selection to MIC_PGA */ + SND_SOC_DAPM_MUX("MIC1 Analog Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic1_analog_control), + SND_SOC_DAPM_MUX("MIC2 Analog Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic2_analog_control), + SND_SOC_DAPM_MUX("MIC3 Analog Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic3_analog_control), + SND_SOC_DAPM_MUX("MIC4 Analog Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic4_analog_control), + + SND_SOC_DAPM_MUX("MIC1M Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic1m_control), + SND_SOC_DAPM_MUX("MIC2M Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic2m_control), + SND_SOC_DAPM_MUX("MIC3M Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic3m_control), + SND_SOC_DAPM_MUX("MIC4M Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic4m_control), + + SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_ADC("CH1_ADC", "CH1 Capture", ADCX140_IN_CH_EN, 7, 0), + SND_SOC_DAPM_ADC("CH2_ADC", "CH2 Capture", ADCX140_IN_CH_EN, 6, 0), + SND_SOC_DAPM_ADC("CH3_ADC", "CH3 Capture", ADCX140_IN_CH_EN, 5, 0), + SND_SOC_DAPM_ADC("CH4_ADC", "CH4 Capture", ADCX140_IN_CH_EN, 4, 0), + + SND_SOC_DAPM_SWITCH("CH1_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch1_en_switch), + SND_SOC_DAPM_SWITCH("CH2_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch2_en_switch), + SND_SOC_DAPM_SWITCH("CH3_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch3_en_switch), + SND_SOC_DAPM_SWITCH("CH4_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch4_en_switch), + + SND_SOC_DAPM_SWITCH("DRE_ENABLE", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_dre_en_switch), + + SND_SOC_DAPM_SWITCH("CH1_DRE_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch1_dre_en_switch), + SND_SOC_DAPM_SWITCH("CH2_DRE_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch2_dre_en_switch), + SND_SOC_DAPM_SWITCH("CH3_DRE_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch3_dre_en_switch), + SND_SOC_DAPM_SWITCH("CH4_DRE_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch4_dre_en_switch), + + SND_SOC_DAPM_MUX("IN1 Analog Mic Resistor", SND_SOC_NOPM, 0, 0, + in1_resistor_controls), + SND_SOC_DAPM_MUX("IN2 Analog Mic Resistor", SND_SOC_NOPM, 0, 0, + in2_resistor_controls), + SND_SOC_DAPM_MUX("IN3 Analog Mic Resistor", SND_SOC_NOPM, 0, 0, + in3_resistor_controls), + SND_SOC_DAPM_MUX("IN4 Analog Mic Resistor", SND_SOC_NOPM, 0, 0, + in4_resistor_controls), + + SND_SOC_DAPM_MUX("Decimation Filter", SND_SOC_NOPM, 0, 0, + decimation_filter_controls), +}; + +static const struct snd_soc_dapm_route adcx140_audio_map[] = { + /* Outputs */ + {"CH1_OUT", NULL, "Output Mixer"}, + {"CH2_OUT", NULL, "Output Mixer"}, + {"CH3_OUT", NULL, "Output Mixer"}, + {"CH4_OUT", NULL, "Output Mixer"}, + + {"CH1_ASI_EN", "Switch", "CH1_ADC"}, + {"CH2_ASI_EN", "Switch", "CH2_ADC"}, + {"CH3_ASI_EN", "Switch", "CH3_ADC"}, + {"CH4_ASI_EN", "Switch", "CH4_ADC"}, + + {"Decimation Filter", "Linear Phase", "DRE_ENABLE"}, + {"Decimation Filter", "Low Latency", "DRE_ENABLE"}, + {"Decimation Filter", "Ultra-low Latency", "DRE_ENABLE"}, + + {"DRE_ENABLE", "Switch", "CH1_DRE_EN"}, + {"DRE_ENABLE", "Switch", "CH2_DRE_EN"}, + {"DRE_ENABLE", "Switch", "CH3_DRE_EN"}, + {"DRE_ENABLE", "Switch", "CH4_DRE_EN"}, + + {"CH1_DRE_EN", "Switch", "CH1_ADC"}, + {"CH2_DRE_EN", "Switch", "CH2_ADC"}, + {"CH3_DRE_EN", "Switch", "CH3_ADC"}, + {"CH4_DRE_EN", "Switch", "CH4_ADC"}, + + /* Mic input */ + {"CH1_ADC", NULL, "MIC_GAIN_CTL_CH1"}, + {"CH2_ADC", NULL, "MIC_GAIN_CTL_CH2"}, + {"CH3_ADC", NULL, "MIC_GAIN_CTL_CH3"}, + {"CH4_ADC", NULL, "MIC_GAIN_CTL_CH4"}, + + {"MIC_GAIN_CTL_CH1", NULL, "IN1 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH1", NULL, "IN1 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH2", NULL, "IN2 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH2", NULL, "IN2 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH3", NULL, "IN3 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH3", NULL, "IN3 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH4", NULL, "IN4 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH4", NULL, "IN4 Analog Mic Resistor"}, + + {"IN1 Analog Mic Resistor", "2.5 kOhm", "MIC1P Input Mux"}, + {"IN1 Analog Mic Resistor", "10 kOhm", "MIC1P Input Mux"}, + {"IN1 Analog Mic Resistor", "20 kOhm", "MIC1P Input Mux"}, + + {"IN1 Analog Mic Resistor", "2.5 kOhm", "MIC1M Input Mux"}, + {"IN1 Analog Mic Resistor", "10 kOhm", "MIC1M Input Mux"}, + {"IN1 Analog Mic Resistor", "20 kOhm", "MIC1M Input Mux"}, + + {"IN2 Analog Mic Resistor", "2.5 kOhm", "MIC2P Input Mux"}, + {"IN2 Analog Mic Resistor", "10 kOhm", "MIC2P Input Mux"}, + {"IN2 Analog Mic Resistor", "20 kOhm", "MIC2P Input Mux"}, + + {"IN2 Analog Mic Resistor", "2.5 kOhm", "MIC2M Input Mux"}, + {"IN2 Analog Mic Resistor", "10 kOhm", "MIC2M Input Mux"}, + {"IN2 Analog Mic Resistor", "20 kOhm", "MIC2M Input Mux"}, + + {"IN3 Analog Mic Resistor", "2.5 kOhm", "MIC3P Input Mux"}, + {"IN3 Analog Mic Resistor", "10 kOhm", "MIC3P Input Mux"}, + {"IN3 Analog Mic Resistor", "20 kOhm", "MIC3P Input Mux"}, + + {"IN3 Analog Mic Resistor", "2.5 kOhm", "MIC3M Input Mux"}, + {"IN3 Analog Mic Resistor", "10 kOhm", "MIC3M Input Mux"}, + {"IN3 Analog Mic Resistor", "20 kOhm", "MIC3M Input Mux"}, + + {"IN4 Analog Mic Resistor", "2.5 kOhm", "MIC4P Input Mux"}, + {"IN4 Analog Mic Resistor", "10 kOhm", "MIC4P Input Mux"}, + {"IN4 Analog Mic Resistor", "20 kOhm", "MIC4P Input Mux"}, + + {"IN4 Analog Mic Resistor", "2.5 kOhm", "MIC4M Input Mux"}, + {"IN4 Analog Mic Resistor", "10 kOhm", "MIC4M Input Mux"}, + {"IN4 Analog Mic Resistor", "20 kOhm", "MIC4M Input Mux"}, + + {"MIC1 Analog Mux", "Line In", "MIC1P"}, + {"MIC2 Analog Mux", "Line In", "MIC2P"}, + {"MIC3 Analog Mux", "Line In", "MIC3P"}, + {"MIC4 Analog Mux", "Line In", "MIC4P"}, + + {"MIC1P Input Mux", "Analog", "MIC1P"}, + {"MIC1M Input Mux", "Analog", "MIC1M"}, + {"MIC2P Input Mux", "Analog", "MIC2P"}, + {"MIC2M Input Mux", "Analog", "MIC2M"}, + {"MIC3P Input Mux", "Analog", "MIC3P"}, + {"MIC3M Input Mux", "Analog", "MIC3M"}, + {"MIC4P Input Mux", "Analog", "MIC4P"}, + {"MIC4M Input Mux", "Analog", "MIC4M"}, +}; + +static const struct snd_kcontrol_new adcx140_snd_controls[] = { + SOC_SINGLE_TLV("Analog CH1 Mic Gain Volume", ADCX140_CH1_CFG1, 2, 42, 0, + adc_tlv), + SOC_SINGLE_TLV("Analog CH2 Mic Gain Volume", ADCX140_CH1_CFG2, 2, 42, 0, + adc_tlv), + SOC_SINGLE_TLV("Analog CH3 Mic Gain Volume", ADCX140_CH1_CFG3, 2, 42, 0, + adc_tlv), + SOC_SINGLE_TLV("Analog CH4 Mic Gain Volume", ADCX140_CH1_CFG4, 2, 42, 0, + adc_tlv), + + SOC_SINGLE_TLV("DRE Threshold", ADCX140_DRE_CFG0, 4, 9, 0, + dre_thresh_tlv), + SOC_SINGLE_TLV("DRE Max Gain", ADCX140_DRE_CFG0, 0, 12, 0, + dre_gain_tlv), + + SOC_SINGLE_TLV("AGC Threshold", ADCX140_AGC_CFG0, 4, 15, 0, + agc_thresh_tlv), + SOC_SINGLE_TLV("AGC Max Gain", ADCX140_AGC_CFG0, 0, 13, 0, + agc_gain_tlv), + + SOC_SINGLE_TLV("Digital CH1 Out Volume", ADCX140_CH1_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH2 Out Volume", ADCX140_CH2_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH3 Out Volume", ADCX140_CH3_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH4 Out Volume", ADCX140_CH4_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH5 Out Volume", ADCX140_CH5_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH6 Out Volume", ADCX140_CH6_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH7 Out Volume", ADCX140_CH7_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH8 Out Volume", ADCX140_CH8_CFG2, + 0, 0xff, 0, dig_vol_tlv), +}; + +static int adcx140_reset(struct adcx140_priv *adcx140) +{ + int ret = 0; + + if (adcx140->gpio_reset) { + gpiod_direction_output(adcx140->gpio_reset, 0); + /* 8.4.1: wait for hw shutdown (25ms) + >= 1ms */ + usleep_range(30000, 100000); + gpiod_direction_output(adcx140->gpio_reset, 1); + } else { + ret = regmap_write(adcx140->regmap, ADCX140_SW_RESET, + ADCX140_RESET); + } + + /* 8.4.2: wait >= 10 ms after entering sleep mode. */ + usleep_range(10000, 100000); + + return 0; +} + +static int adcx140_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; + u8 data = 0; + + switch (params_width(params)) { + case 16: + data = ADCX140_16_BIT_WORD; + break; + case 20: + data = ADCX140_20_BIT_WORD; + break; + case 24: + data = ADCX140_24_BIT_WORD; + break; + case 32: + data = ADCX140_32_BIT_WORD; + break; + default: + dev_err(component->dev, "%s: Unsupported width %d\n", + __func__, params_width(params)); + return -EINVAL; + } + + snd_soc_component_update_bits(component, ADCX140_ASI_CFG0, + ADCX140_WORD_LEN_MSK, data); + + return 0; +} + +static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + u8 iface_reg1 = 0; + u8 iface_reg2 = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface_reg2 |= ADCX140_BCLK_FSYNC_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(component->dev, "Invalid DAI master/slave interface\n"); + return -EINVAL; + } + + /* signal polarity */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + iface_reg1 |= ADCX140_FSYNCINV_BIT; + break; + case SND_SOC_DAIFMT_IB_IF: + iface_reg1 |= ADCX140_BCLKINV_BIT | ADCX140_FSYNCINV_BIT; + break; + case SND_SOC_DAIFMT_IB_NF: + iface_reg1 |= ADCX140_BCLKINV_BIT; + break; + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(component->dev, "Invalid DAI clock signal polarity\n"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface_reg1 |= ADCX140_I2S_MODE_BIT; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg1 |= ADCX140_LEFT_JUST_BIT; + break; + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + break; + default: + dev_err(component->dev, "Invalid DAI interface format\n"); + return -EINVAL; + } + + adcx140->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + snd_soc_component_update_bits(component, ADCX140_ASI_CFG0, + ADCX140_FSYNCINV_BIT | + ADCX140_BCLKINV_BIT | + ADCX140_ASI_FORMAT_MSK, + iface_reg1); + snd_soc_component_update_bits(component, ADCX140_MST_CFG0, + ADCX140_BCLK_FSYNC_MASTER, iface_reg2); + + return 0; +} + +static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = codec_dai->component; + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + unsigned int lsb; + + if (tx_mask != rx_mask) { + dev_err(component->dev, "tx and rx masks must be symmetric\n"); + return -EINVAL; + } + + /* TDM based on DSP mode requires slots to be adjacent */ + lsb = __ffs(tx_mask); + if ((lsb + 1) != __fls(tx_mask)) { + dev_err(component->dev, "Invalid mask, slots must be adjacent\n"); + return -EINVAL; + } + + switch (slot_width) { + case 16: + case 20: + case 24: + case 32: + break; + default: + dev_err(component->dev, "Unsupported slot width %d\n", slot_width); + return -EINVAL; + } + + adcx140->tdm_delay = lsb; + adcx140->slot_width = slot_width; + + return 0; +} + +static int adcx140_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + int offset = 0; + int width = adcx140->slot_width; + + if (!width) + width = substream->runtime->sample_bits; + + /* TDM slot selection only valid in DSP_A/_B mode */ + if (adcx140->dai_fmt == SND_SOC_DAIFMT_DSP_A) + offset += (adcx140->tdm_delay * width + 1); + else if (adcx140->dai_fmt == SND_SOC_DAIFMT_DSP_B) + offset += adcx140->tdm_delay * width; + + /* Configure data offset */ + snd_soc_component_update_bits(component, ADCX140_ASI_CFG1, + ADCX140_TX_OFFSET_MASK, offset); + + return 0; +} + +static const struct snd_soc_dai_ops adcx140_dai_ops = { + .hw_params = adcx140_hw_params, + .set_fmt = adcx140_set_dai_fmt, + .prepare = adcx140_prepare, + .set_tdm_slot = adcx140_set_dai_tdm_slot, +}; + +static int adcx140_codec_probe(struct snd_soc_component *component) +{ + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + int sleep_cfg_val = ADCX140_WAKE_DEV; + u8 bias_source; + u8 vref_source; + int ret; + + ret = device_property_read_u8(adcx140->dev, "ti,mic-bias-source", + &bias_source); + if (ret) + bias_source = ADCX140_MIC_BIAS_VAL_VREF; + + if (bias_source < ADCX140_MIC_BIAS_VAL_VREF || + bias_source > ADCX140_MIC_BIAS_VAL_AVDD) { + dev_err(adcx140->dev, "Mic Bias source value is invalid\n"); + return -EINVAL; + } + + ret = device_property_read_u8(adcx140->dev, "ti,vref-source", + &vref_source); + if (ret) + vref_source = ADCX140_MIC_BIAS_VREF_275V; + + if (vref_source < ADCX140_MIC_BIAS_VREF_275V || + vref_source > ADCX140_MIC_BIAS_VREF_1375V) { + dev_err(adcx140->dev, "Mic Bias source value is invalid\n"); + return -EINVAL; + } + + bias_source |= vref_source; + + ret = adcx140_reset(adcx140); + if (ret) + goto out; + + if(adcx140->supply_areg == NULL) + sleep_cfg_val |= ADCX140_AREG_INTERNAL; + + ret = regmap_write(adcx140->regmap, ADCX140_SLEEP_CFG, sleep_cfg_val); + if (ret) { + dev_err(adcx140->dev, "setting sleep config failed %d\n", ret); + goto out; + } + + /* 8.4.3: Wait >= 1ms after entering active mode. */ + usleep_range(1000, 100000); + + ret = regmap_update_bits(adcx140->regmap, ADCX140_BIAS_CFG, + ADCX140_MIC_BIAS_VAL_MSK | + ADCX140_MIC_BIAS_VREF_MSK, bias_source); + if (ret) + dev_err(adcx140->dev, "setting MIC bias failed %d\n", ret); +out: + return ret; +} + +static int adcx140_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + int pwr_cfg = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + case SND_SOC_BIAS_STANDBY: + pwr_cfg = ADCX140_PWR_CFG_BIAS_PDZ | ADCX140_PWR_CFG_PLL_PDZ | + ADCX140_PWR_CFG_ADC_PDZ; + break; + case SND_SOC_BIAS_OFF: + pwr_cfg = 0x0; + break; + } + + return regmap_write(adcx140->regmap, ADCX140_PWR_CFG, pwr_cfg); +} + +static const struct snd_soc_component_driver soc_codec_driver_adcx140 = { + .probe = adcx140_codec_probe, + .set_bias_level = adcx140_set_bias_level, + .controls = adcx140_snd_controls, + .num_controls = ARRAY_SIZE(adcx140_snd_controls), + .dapm_widgets = adcx140_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adcx140_dapm_widgets), + .dapm_routes = adcx140_audio_map, + .num_dapm_routes = ARRAY_SIZE(adcx140_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 0, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static struct snd_soc_dai_driver adcx140_dai_driver[] = { + { + .name = "tlv320adcx140-codec", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = ADCX140_MAX_CHANNELS, + .rates = ADCX140_RATES, + .formats = ADCX140_FORMATS, + }, + .ops = &adcx140_dai_ops, + .symmetric_rates = 1, + } +}; + +static const struct of_device_id tlv320adcx140_of_match[] = { + { .compatible = "ti,tlv320adc3140" }, + { .compatible = "ti,tlv320adc5140" }, + { .compatible = "ti,tlv320adc6140" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tlv320adcx140_of_match); + +static int adcx140_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct adcx140_priv *adcx140; + int ret; + + adcx140 = devm_kzalloc(&i2c->dev, sizeof(*adcx140), GFP_KERNEL); + if (!adcx140) + return -ENOMEM; + + adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(adcx140->gpio_reset)) + dev_info(&i2c->dev, "Reset GPIO not defined\n"); + + adcx140->supply_areg = devm_regulator_get_optional(adcx140->dev, + "areg"); + if (IS_ERR(adcx140->supply_areg)) { + if (PTR_ERR(adcx140->supply_areg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else + adcx140->supply_areg = NULL; + } else { + ret = regulator_enable(adcx140->supply_areg); + if (ret) { + dev_err(adcx140->dev, "Failed to enable areg\n"); + return ret; + } + } + + adcx140->regmap = devm_regmap_init_i2c(i2c, &adcx140_i2c_regmap); + if (IS_ERR(adcx140->regmap)) { + ret = PTR_ERR(adcx140->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + adcx140->dev = &i2c->dev; + i2c_set_clientdata(i2c, adcx140); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_codec_driver_adcx140, + adcx140_dai_driver, 1); +} + +static const struct i2c_device_id adcx140_i2c_id[] = { + { "tlv320adc3140", 0 }, + { "tlv320adc5140", 1 }, + { "tlv320adc6140", 2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adcx140_i2c_id); + +static struct i2c_driver adcx140_i2c_driver = { + .driver = { + .name = "tlv320adcx140-codec", + .of_match_table = of_match_ptr(tlv320adcx140_of_match), + }, + .probe = adcx140_i2c_probe, + .id_table = adcx140_i2c_id, +}; +module_i2c_driver(adcx140_i2c_driver); + +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); +MODULE_DESCRIPTION("ASoC TLV320ADCX140 CODEC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tlv320adcx140.h b/sound/soc/codecs/tlv320adcx140.h new file mode 100644 index 000000000000..6d055e55909e --- /dev/null +++ b/sound/soc/codecs/tlv320adcx140.h @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +// TLV320ADCX104 Sound driver +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + +#ifndef _TLV320ADCX140_H +#define _TLV320ADCX140_H + +#define ADCX140_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define ADCX140_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define ADCX140_PAGE_SELECT 0x00 +#define ADCX140_SW_RESET 0x01 +#define ADCX140_SLEEP_CFG 0x02 +#define ADCX140_SHDN_CFG 0x05 +#define ADCX140_ASI_CFG0 0x07 +#define ADCX140_ASI_CFG1 0x08 +#define ADCX140_ASI_CFG2 0x09 +#define ADCX140_ASI_CH1 0x0b +#define ADCX140_ASI_CH2 0x0c +#define ADCX140_ASI_CH3 0x0d +#define ADCX140_ASI_CH4 0x0e +#define ADCX140_ASI_CH5 0x0f +#define ADCX140_ASI_CH6 0x10 +#define ADCX140_ASI_CH7 0x11 +#define ADCX140_ASI_CH8 0x12 +#define ADCX140_MST_CFG0 0x13 +#define ADCX140_MST_CFG1 0x14 +#define ADCX140_ASI_STS 0x15 +#define ADCX140_CLK_SRC 0x16 +#define ADCX140_PDMCLK_CFG 0x1f +#define ADCX140_PDM_CFG 0x20 +#define ADCX140_GPIO_CFG0 0x21 +#define ADCX140_GPO_CFG1 0x22 +#define ADCX140_GPO_CFG2 0x23 +#define ADCX140_GPO_CFG3 0x24 +#define ADCX140_GPO_CFG4 0x25 +#define ADCX140_GPO_VAL 0x29 +#define ADCX140_GPIO_MON 0x2a +#define ADCX140_GPI_CFG0 0x2b +#define ADCX140_GPI_CFG1 0x2c +#define ADCX140_GPI_MON 0x2f +#define ADCX140_INT_CFG 0x32 +#define ADCX140_INT_MASK0 0x33 +#define ADCX140_INT_LTCH0 0x36 +#define ADCX140_BIAS_CFG 0x3b +#define ADCX140_CH1_CFG0 0x3c +#define ADCX140_CH1_CFG1 0x3d +#define ADCX140_CH1_CFG2 0x3e +#define ADCX140_CH1_CFG3 0x3f +#define ADCX140_CH1_CFG4 0x40 +#define ADCX140_CH2_CFG0 0x41 +#define ADCX140_CH2_CFG1 0x42 +#define ADCX140_CH2_CFG2 0x43 +#define ADCX140_CH2_CFG3 0x44 +#define ADCX140_CH2_CFG4 0x45 +#define ADCX140_CH3_CFG0 0x46 +#define ADCX140_CH3_CFG1 0x47 +#define ADCX140_CH3_CFG2 0x48 +#define ADCX140_CH3_CFG3 0x49 +#define ADCX140_CH3_CFG4 0x4a +#define ADCX140_CH4_CFG0 0x4b +#define ADCX140_CH4_CFG1 0x4c +#define ADCX140_CH4_CFG2 0x4d +#define ADCX140_CH4_CFG3 0x4e +#define ADCX140_CH4_CFG4 0x4f +#define ADCX140_CH5_CFG2 0x52 +#define ADCX140_CH5_CFG3 0x53 +#define ADCX140_CH5_CFG4 0x54 +#define ADCX140_CH6_CFG2 0x57 +#define ADCX140_CH6_CFG3 0x58 +#define ADCX140_CH6_CFG4 0x59 +#define ADCX140_CH7_CFG2 0x5c +#define ADCX140_CH7_CFG3 0x5d +#define ADCX140_CH7_CFG4 0x5e +#define ADCX140_CH8_CFG2 0x61 +#define ADCX140_CH8_CFG3 0x62 +#define ADCX140_CH8_CFG4 0x63 +#define ADCX140_DSP_CFG0 0x6b +#define ADCX140_DSP_CFG1 0x6c +#define ADCX140_DRE_CFG0 0x6d +#define ADCX140_AGC_CFG0 0x70 +#define ADCX140_IN_CH_EN 0x73 +#define ADCX140_ASI_OUT_CH_EN 0x74 +#define ADCX140_PWR_CFG 0x75 +#define ADCX140_DEV_STS0 0x76 +#define ADCX140_DEV_STS1 0x77 + +#define ADCX140_RESET BIT(0) + +#define ADCX140_WAKE_DEV BIT(0) +#define ADCX140_AREG_INTERNAL BIT(7) + +#define ADCX140_BCLKINV_BIT BIT(2) +#define ADCX140_FSYNCINV_BIT BIT(3) +#define ADCX140_INV_MSK (ADCX140_BCLKINV_BIT | ADCX140_FSYNCINV_BIT) +#define ADCX140_BCLK_FSYNC_MASTER BIT(7) +#define ADCX140_I2S_MODE_BIT BIT(6) +#define ADCX140_LEFT_JUST_BIT BIT(7) +#define ADCX140_ASI_FORMAT_MSK (ADCX140_I2S_MODE_BIT | ADCX140_LEFT_JUST_BIT) + +#define ADCX140_16_BIT_WORD 0x0 +#define ADCX140_20_BIT_WORD BIT(4) +#define ADCX140_24_BIT_WORD BIT(5) +#define ADCX140_32_BIT_WORD (BIT(4) | BIT(5)) +#define ADCX140_WORD_LEN_MSK 0x30 + +#define ADCX140_MAX_CHANNELS 8 + +#define ADCX140_MIC_BIAS_VAL_VREF 0 +#define ADCX140_MIC_BIAS_VAL_VREF_1096 1 +#define ADCX140_MIC_BIAS_VAL_AVDD 6 +#define ADCX140_MIC_BIAS_VAL_MSK GENMASK(6, 4) + +#define ADCX140_MIC_BIAS_VREF_275V 0 +#define ADCX140_MIC_BIAS_VREF_25V 1 +#define ADCX140_MIC_BIAS_VREF_1375V 2 +#define ADCX140_MIC_BIAS_VREF_MSK GENMASK(1, 0) + +#define ADCX140_PWR_CFG_BIAS_PDZ BIT(7) +#define ADCX140_PWR_CFG_ADC_PDZ BIT(6) +#define ADCX140_PWR_CFG_PLL_PDZ BIT(5) + +#define ADCX140_TX_OFFSET_MASK GENMASK(4, 0) + +#endif /* _TLV320ADCX140_ */ diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index f11ffa28683b..700cc1212770 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -4926,11 +4926,11 @@ static const struct regmap_range_cfg wcd9335_ranges[] = { .name = "WCD9335", .range_min = 0x0, .range_max = WCD9335_MAX_REGISTER, - .selector_reg = WCD9335_REG(0x0, 0), + .selector_reg = WCD9335_SEL_REGISTER, .selector_mask = 0xff, .selector_shift = 0, - .window_start = 0x0, - .window_len = 0x1000, + .window_start = 0x800, + .window_len = 0x100, }, }; @@ -4968,12 +4968,12 @@ static const struct regmap_range_cfg wcd9335_ifc_ranges[] = { { .name = "WCD9335-IFC-DEV", .range_min = 0x0, - .range_max = WCD9335_REG(0, 0x7ff), - .selector_reg = WCD9335_REG(0, 0x0), - .selector_mask = 0xff, + .range_max = WCD9335_MAX_REGISTER, + .selector_reg = WCD9335_SEL_REGISTER, + .selector_mask = 0xfff, .selector_shift = 0, - .window_start = 0x0, - .window_len = 0x1000, + .window_start = 0x800, + .window_len = 0x400, }, }; @@ -4981,7 +4981,7 @@ static struct regmap_config wcd9335_ifc_regmap_config = { .reg_bits = 16, .val_bits = 8, .can_multi_write = true, - .max_register = WCD9335_REG(0, 0x7FF), + .max_register = WCD9335_MAX_REGISTER, .ranges = wcd9335_ifc_ranges, .num_ranges = ARRAY_SIZE(wcd9335_ifc_ranges), }; diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h index 4d9be2496c30..72060824c743 100644 --- a/sound/soc/codecs/wcd9335.h +++ b/sound/soc/codecs/wcd9335.h @@ -8,9 +8,9 @@ * in slimbus mode the reg base starts from 0x800 * in i2s/i2c mode the reg base is 0x0 */ -#define WCD9335_REG(pg, r) ((pg << 12) | (r) | 0x800) +#define WCD9335_REG(pg, r) ((pg << 8) | (r)) #define WCD9335_REG_OFFSET(r) (r & 0xFF) -#define WCD9335_PAGE_OFFSET(r) ((r >> 12) & 0xFF) +#define WCD9335_PAGE_OFFSET(r) ((r >> 8) & 0xFF) /* Page-0 Registers */ #define WCD9335_PAGE0_PAGE_REGISTER WCD9335_REG(0x00, 0x000) @@ -600,7 +600,8 @@ #define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_ENABLE BIT(0) #define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_DISABLE 0 #define WCD9335_CDC_TOP_TOP_CFG1 WCD9335_REG(0x0d, 0x082) -#define WCD9335_MAX_REGISTER WCD9335_REG(0x80, 0x0FF) +#define WCD9335_MAX_REGISTER 0xffff +#define WCD9335_SEL_REGISTER 0x800 /* SLIMBUS Slave Registers */ #define WCD9335_SLIM_PGD_PORT_INT_EN0 WCD9335_REG(0, 0x30) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 158e878abd6c..5269857e2746 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -3,7 +3,6 @@ #include <linux/clk.h> #include <linux/clk-provider.h> -#include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/mfd/wcd934x/registers.h> @@ -11,10 +10,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of_clk.h> -#include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/of.h> -#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -1202,11 +1198,6 @@ static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) regmap_update_bits(wcd->regmap, WCD934X_ANA_RCO, WCD934X_ANA_RCO_BG_EN_MASK, 0); usleep_range(100, 110); - } else if (sido_src == SIDO_SOURCE_RCO_BG) { - regmap_update_bits(wcd->regmap, WCD934X_ANA_RCO, - WCD934X_ANA_RCO_BG_EN_MASK, - WCD934X_ANA_RCO_BG_ENABLE); - usleep_range(100, 110); regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, WCD934X_ANA_BUCK_PRE_EN1_MASK, WCD934X_ANA_BUCK_PRE_EN1_ENABLE); @@ -1219,6 +1210,11 @@ static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) WCD934X_ANA_BUCK_HI_ACCU_EN_MASK, WCD934X_ANA_BUCK_HI_ACCU_ENABLE); usleep_range(100, 110); + } else if (sido_src == SIDO_SOURCE_RCO_BG) { + regmap_update_bits(wcd->regmap, WCD934X_ANA_RCO, + WCD934X_ANA_RCO_BG_EN_MASK, + WCD934X_ANA_RCO_BG_ENABLE); + usleep_range(100, 110); } wcd->sido_input_src = sido_src; @@ -1883,20 +1879,16 @@ static int wcd934x_set_channel_map(struct snd_soc_dai *dai, return -EINVAL; } - if (wcd->rx_chs) { - wcd->num_rx_port = rx_num; - for (i = 0; i < rx_num; i++) { - wcd->rx_chs[i].ch_num = rx_slot[i]; - INIT_LIST_HEAD(&wcd->rx_chs[i].list); - } + wcd->num_rx_port = rx_num; + for (i = 0; i < rx_num; i++) { + wcd->rx_chs[i].ch_num = rx_slot[i]; + INIT_LIST_HEAD(&wcd->rx_chs[i].list); } - if (wcd->tx_chs) { - wcd->num_tx_port = tx_num; - for (i = 0; i < tx_num; i++) { - wcd->tx_chs[i].ch_num = tx_slot[i]; - INIT_LIST_HEAD(&wcd->tx_chs[i].list); - } + wcd->num_tx_port = tx_num; + for (i = 0; i < tx_num; i++) { + wcd->tx_chs[i].ch_num = tx_slot[i]; + INIT_LIST_HEAD(&wcd->tx_chs[i].list); } return 0; @@ -3392,18 +3384,15 @@ static void wcd934x_codec_hphdelay_lutbypass(struct snd_soc_component *comp, { u8 hph_dly_mask; u16 hph_lut_bypass_reg = 0; - u16 hph_comp_ctrl7 = 0; switch (interp_idx) { case INTERP_HPHL: hph_dly_mask = 1; hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT; - hph_comp_ctrl7 = WCD934X_CDC_COMPANDER1_CTL7; break; case INTERP_HPHR: hph_dly_mask = 2; hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT; - hph_comp_ctrl7 = WCD934X_CDC_COMPANDER2_CTL7; break; default: return; diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 727d6703c905..fbcee21736e8 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -43,7 +43,7 @@ struct dfw_binrec { u8 command; u32 length:24; u32 address; - uint8_t data[0]; + uint8_t data[]; } __packed; struct dfw_inforec { diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 9dc215b5c504..499e87d1dfcc 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2245,14 +2245,14 @@ static int wm5110_open(struct snd_compr_stream *stream) struct arizona *arizona = priv->core.arizona; int n_adsp; - if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) { + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "wm5110-dsp-voicectrl") == 0) { n_adsp = 2; - } else if (strcmp(rtd->codec_dai->name, "wm5110-dsp-trace") == 0) { + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "wm5110-dsp-trace") == 0) { n_adsp = 0; } else { dev_err(arizona->dev, "No suitable compressed stream for DAI '%s'\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); return -EINVAL; } diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index dc4fe4f5239d..06ba36595ddd 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -196,14 +196,6 @@ SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0), SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0), }; -/* AUX Input boost vol */ -static const struct snd_kcontrol_new wm8974_aux_boost_controls = -SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0); - -/* Mic Input boost vol */ -static const struct snd_kcontrol_new wm8974_mic_boost_controls = -SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0); - static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = { SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0, &wm8974_speaker_mixer_controls[0], diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d3d32b501aca..1ef69409ccd1 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1436,12 +1436,12 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, subname = NULL; /* don't append subname */ break; case 2: - ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s%c %.12s %x", dsp->name, *region_name, wm_adsp_fw_text[dsp->fw], alg_region->alg); break; default: - ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %.12s %x", dsp->name, wm_adsp_fw_text[dsp->fw], alg_region->alg); break; @@ -3467,22 +3467,22 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) if (wm_adsp_fw[dsp->fw].num_caps == 0) { adsp_err(dsp, "%s: Firmware does not support compressed API\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); ret = -ENXIO; goto out; } if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { adsp_err(dsp, "%s: Firmware does not support stream direction\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); ret = -EINVAL; goto out; } list_for_each_entry(tmp, &dsp->compr_list, list) { - if (!strcmp(tmp->name, rtd->codec_dai->name)) { + if (!strcmp(tmp->name, asoc_rtd_to_codec(rtd, 0)->name)) { adsp_err(dsp, "%s: Only a single stream supported per dai\n", - rtd->codec_dai->name); + asoc_rtd_to_codec(rtd, 0)->name); ret = -EBUSY; goto out; } @@ -3496,7 +3496,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) compr->dsp = dsp; compr->stream = stream; - compr->name = rtd->codec_dai->name; + compr->name = asoc_rtd_to_codec(rtd, 0)->name; list_add_tail(&compr->list, &dsp->compr_list); diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index b59f1d0e7f84..f2d6f2f81f14 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -676,7 +676,6 @@ struct wsa881x_priv { int active_ports; bool port_prepared[WSA881X_MAX_SWR_PORTS]; bool port_enable[WSA881X_MAX_SWR_PORTS]; - bool stream_prepared; }; static void wsa881x_init(struct wsa881x_priv *wsa881x) @@ -954,41 +953,6 @@ static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("SPKR"), }; -static int wsa881x_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); - int ret; - - if (wsa881x->stream_prepared) { - sdw_disable_stream(wsa881x->sruntime); - sdw_deprepare_stream(wsa881x->sruntime); - wsa881x->stream_prepared = false; - } - - - ret = sdw_prepare_stream(wsa881x->sruntime); - if (ret) - return ret; - - /** - * NOTE: there is a strict hw requirement about the ordering of port - * enables and actual PA enable. PA enable should only happen after - * soundwire ports are enabled if not DC on the line is accumulated - * resulting in Click/Pop Noise - * PA enable/mute are handled as part of DAPM and digital mute. - */ - - ret = sdw_enable_stream(wsa881x->sruntime); - if (ret) { - sdw_deprepare_stream(wsa881x->sruntime); - return ret; - } - wsa881x->stream_prepared = true; - - return ret; -} - static int wsa881x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1016,12 +980,7 @@ static int wsa881x_hw_free(struct snd_pcm_substream *substream, { struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); - if (wsa881x->stream_prepared) { - sdw_disable_stream(wsa881x->sruntime); - sdw_deprepare_stream(wsa881x->sruntime); - sdw_stream_remove_slave(wsa881x->slave, wsa881x->sruntime); - wsa881x->stream_prepared = false; - } + sdw_stream_remove_slave(wsa881x->slave, wsa881x->sruntime); return 0; } @@ -1052,7 +1011,6 @@ static int wsa881x_digital_mute(struct snd_soc_dai *dai, int mute, int stream) static struct snd_soc_dai_ops wsa881x_dai_ops = { .hw_params = wsa881x_hw_params, - .prepare = wsa881x_prepare, .hw_free = wsa881x_hw_free, .mute_stream = wsa881x_digital_mute, .set_sdw_stream = wsa881x_set_sdw_stream, @@ -1150,7 +1108,7 @@ static int wsa881x_probe(struct sdw_slave *pdev, wsa881x->sconfig.type = SDW_STREAM_PDM; pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS, 0); pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; - gpiod_set_value(wsa881x->sd_n, 1); + gpiod_direction_output(wsa881x->sd_n, 1); wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config); if (IS_ERR(wsa881x->regmap)) { diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 7eeca2150b2d..515f88456dbd 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -422,15 +422,15 @@ static int dw_i2s_resume(struct snd_soc_component *component) { struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component); struct snd_soc_dai *dai; + int stream; if (dev->capability & DW_I2S_MASTER) clk_enable(dev->clk); for_each_component_dais(component, dai) { - if (dai->playback_active) - dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK); - if (dai->capture_active) - dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE); + for_each_pcm_streams(stream) + if (dai->stream_active[stream]) + dw_i2s_config(dev, stream); } return 0; diff --git a/sound/soc/dwc/dwc-pcm.c b/sound/soc/dwc/dwc-pcm.c index 4b25aca3804f..9868e7373d36 100644 --- a/sound/soc/dwc/dwc-pcm.c +++ b/sound/soc/dwc/dwc-pcm.c @@ -140,7 +140,7 @@ static int dw_pcm_open(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); snd_soc_set_runtime_hwparams(substream, &dw_pcm_hardware); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 6f3b768489f6..4ff2d21bb32f 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -31,8 +31,8 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, 0, diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 9ce55feaac22..bb33601fab84 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -159,7 +159,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, return 0; /* Specific configurations of DAIs starts from here */ - ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx], + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), cpu_priv->sysclk_id[tx], cpu_priv->sysclk_freq[tx], cpu_priv->sysclk_dir[tx]); if (ret && ret != -ENOTSUPP) { @@ -168,7 +168,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, } if (cpu_priv->slot_width) { - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, cpu_priv->slot_width); if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set TDM slot for cpu dai\n"); @@ -257,7 +257,7 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - codec_dai = rtd->codec_dai; + codec_dai = asoc_rtd_to_codec(rtd, 0); if (dapm->dev != codec_dai->dev) return 0; @@ -446,14 +446,14 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); struct snd_soc_pcm_runtime *rtd = list_first_entry( &card->rtd_list, struct snd_soc_pcm_runtime, list); - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct codec_priv *codec_priv = &priv->codec_priv; struct device *dev = card->dev; int ret; if (fsl_asoc_card_is_ac97(priv)) { #if IS_ENABLED(CONFIG_SND_AC97_CODEC) - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); /* diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index ece130f59d15..e7178817d7a7 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -152,7 +152,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, for_each_dpcm_be(rtd, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; struct snd_pcm_substream *substream_be; - struct snd_soc_dai *dai = be->cpu_dai; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(be, 0); if (dpcm->fe != rtd) continue; @@ -169,7 +169,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, } /* Override dma_data of the Front-End and config its dmaengine */ - dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dma_params_fe = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); dma_params_fe->addr = asrc_priv->paddr + REG_ASRDx(!dir, index); dma_params_fe->maxburst = dma_params_be->maxburst; @@ -328,7 +328,7 @@ static int fsl_asrc_dma_startup(struct snd_soc_component *component, goto dma_chan_err; } - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); /* Refine the snd_imx_hardware according to caps of DMA. */ ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream, @@ -400,7 +400,7 @@ static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component, return ret; } - for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) { + for_each_pcm_streams(i) { substream = pcm->streams[i].substream; if (!substream) continue; @@ -428,7 +428,7 @@ static void fsl_asrc_dma_pcm_free(struct snd_soc_component *component, struct snd_pcm_substream *substream; int i; - for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) { + for_each_pcm_streams(i) { substream = pcm->streams[i].substream; if (!substream) continue; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 7858a5499ac5..c711d2d93280 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -370,7 +370,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, int sample_rate) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; struct regmap *regmap = spdif_priv->regmap; struct platform_device *pdev = spdif_priv->pdev; @@ -458,7 +458,7 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct platform_device *pdev = spdif_priv->pdev; struct regmap *regmap = spdif_priv->regmap; u32 scr, mask; @@ -534,7 +534,7 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct regmap *regmap = spdif_priv->regmap; u32 scr, mask, i; @@ -569,7 +569,7 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; struct platform_device *pdev = spdif_priv->pdev; u32 sample_rate = params_rate(params); @@ -597,7 +597,7 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct regmap *regmap = spdif_priv->regmap; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 intr = SIE_INTR_FOR(tx); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 5c97269be346..bad89b0d129e 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -631,7 +631,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); int ret; ret = clk_prepare_enable(ssi->clk); @@ -655,7 +655,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); clk_disable_unprepare(ssi->clk); } @@ -854,7 +854,7 @@ static int fsl_ssi_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); if (fsl_ssi_is_i2s_master(ssi) && ssi->baudclk_streams & BIT(substream->stream)) { @@ -1059,7 +1059,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; switch (cmd) { diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index 5ef6881395e0..e09b45de0efd 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -85,13 +85,13 @@ static int imx_audmix_fe_hw_params(struct snd_pcm_substream *substream, dir = tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN; /* set DAI configuration */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt); + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), fmt); if (ret) { dev_err(dev, "failed to set cpu dai fmt: %d\n", ret); return ret; } - ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, FSL_SAI_CLK_MAST1, 0, dir); + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), FSL_SAI_CLK_MAST1, 0, dir); if (ret) { dev_err(dev, "failed to set cpu sysclk: %d\n", ret); return ret; @@ -101,7 +101,7 @@ static int imx_audmix_fe_hw_params(struct snd_pcm_substream *substream, * Per datasheet, AUDMIX expects 8 slots and 32 bits * for every slot in TDM mode. */ - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, BIT(channels) - 1, + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), BIT(channels) - 1, BIT(channels) - 1, 8, 32); if (ret) dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret); @@ -125,7 +125,7 @@ static int imx_audmix_be_hw_params(struct snd_pcm_substream *substream, fmt |= SND_SOC_DAIFMT_CBM_CFM; /* set AUDMIX DAI configuration */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt); + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), fmt); if (ret) dev_err(dev, "failed to set AUDMIX DAI fmt: %d\n", ret); diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c index 2b679680c93f..fab2d6c56653 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -27,8 +27,8 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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; ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 4, 16); diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 15e8b9343c35..f45cb4bbb6c4 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -30,7 +30,7 @@ static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd) struct device *dev = rtd->card->dev; int ret; - ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), SGTL5000_SYSCLK, data->clk_frequency, SND_SOC_CLOCK_IN); if (ret) { dev_err(dev, "could not set codec driver clock params\n"); diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index ed7211d744b3..3b8c796d7829 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -115,7 +115,7 @@ static int psc_dma_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct snd_pcm_runtime *runtime = substream->runtime; struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; @@ -217,7 +217,7 @@ static int psc_dma_open(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct psc_dma_stream *s; int rc; @@ -245,7 +245,7 @@ static int psc_dma_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct psc_dma_stream *s; dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); @@ -271,7 +271,7 @@ psc_dma_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct psc_dma_stream *s; dma_addr_t count; @@ -298,7 +298,7 @@ static int psc_dma_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; - struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); struct snd_pcm *pcm = rtd->pcm; size_t size = psc_dma_hardware.buffer_bytes_max; int rc; diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 9bc01f374b39..1ab4fbda08cb 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -39,7 +39,7 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); u32 mode; dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 23617eb09ba1..f7bd90051ce7 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -105,7 +105,7 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) int ret = 0; /* Tell the codec driver what the serial protocol is. */ - ret = snd_soc_dai_set_fmt(rtd->codec_dai, machine_data->dai_format); + ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), machine_data->dai_format); if (ret < 0) { dev_err(dev, "could not set codec driver audio format\n"); return ret; @@ -115,7 +115,7 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) * Tell the codec driver what the MCLK frequency is, and whether it's * a slave or master. */ - ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, machine_data->clk_frequency, machine_data->codec_clk_direction); if (ret < 0) { diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c index 38ac4a397742..a36d4e8cd55c 100644 --- a/sound/soc/fsl/mx27vis-aic32x4.c +++ b/sound/soc/fsl/mx27vis-aic32x4.c @@ -37,8 +37,8 @@ static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, 0, diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index 6114b01b90f7..fe3091590f20 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -128,7 +128,7 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) int ret = 0; /* Tell the codec driver what the serial protocol is. */ - ret = snd_soc_dai_set_fmt(rtd->codec_dai, mdata->dai_format); + ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), mdata->dai_format); if (ret < 0) { dev_err(dev, "could not set codec driver audio format\n"); return ret; @@ -138,7 +138,7 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) * Tell the codec driver what the MCLK frequency is, and whether it's * a slave or master. */ - ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, mdata->clk_frequency, + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, mdata->clk_frequency, mdata->codec_clk_direction); if (ret < 0) { dev_err(dev, "could not set codec driver clock params\n"); diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c index 72687235c0ae..f5374fe354ab 100644 --- a/sound/soc/fsl/p1022_rdk.c +++ b/sound/soc/fsl/p1022_rdk.c @@ -134,14 +134,14 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream) int ret = 0; /* Tell the codec driver what the serial protocol is. */ - ret = snd_soc_dai_set_fmt(rtd->codec_dai, mdata->dai_format); + ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), mdata->dai_format); if (ret < 0) { dev_err(dev, "could not set codec driver audio format (ret=%i)\n", ret); return ret; } - ret = snd_soc_dai_set_pll(rtd->codec_dai, 0, 0, mdata->clk_frequency, + ret = snd_soc_dai_set_pll(asoc_rtd_to_codec(rtd, 0), 0, 0, mdata->clk_frequency, mdata->clk_frequency); if (ret < 0) { dev_err(dev, "could not set codec PLL frequency (ret=%i)\n", diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c index 52d321bede9c..8b1551c55452 100644 --- a/sound/soc/fsl/wm1133-ev1.c +++ b/sound/soc/fsl/wm1133-ev1.c @@ -76,8 +76,8 @@ static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int i, found = 0; snd_pcm_format_t format = params_format(params); unsigned int rate = params_rate(params); @@ -196,7 +196,7 @@ static struct snd_soc_jack_pin mic_jack_pins[] = { static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; /* Headphone jack detection */ snd_soc_card_jack_new(rtd->card, "Headphone", SND_JACK_HEADPHONE, diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 9b794775df53..8c54dc6710fe 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -213,8 +213,8 @@ EXPORT_SYMBOL_GPL(asoc_simple_startup); void asoc_simple_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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 asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); @@ -249,8 +249,8 @@ int asoc_simple_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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 asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); @@ -331,22 +331,70 @@ static int asoc_simple_init_dai(struct snd_soc_dai *dai, return 0; } +static int asoc_simple_init_dai_link_params(struct snd_soc_pcm_runtime *rtd, + struct simple_dai_props *dai_props) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_soc_component *component; + struct snd_soc_pcm_stream *params; + struct snd_pcm_hardware hw; + int i, ret, stream; + + /* Only codecs should have non_legacy_dai_naming set. */ + for_each_rtd_components(rtd, i, component) { + if (!component->driver->non_legacy_dai_naming) + return 0; + } + + /* Assumes the capabilities are the same for all supported streams */ + for_each_pcm_streams(stream) { + ret = snd_soc_runtime_calc_hw(rtd, &hw, stream); + if (ret == 0) + break; + } + + if (ret < 0) { + dev_err(rtd->dev, "simple-card: no valid dai_link params\n"); + return ret; + } + + params = devm_kzalloc(rtd->dev, sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + params->formats = hw.formats; + params->rates = hw.rates; + params->rate_min = hw.rate_min; + params->rate_max = hw.rate_max; + params->channels_min = hw.channels_min; + params->channels_max = hw.channels_max; + + dai_link->params = params; + dai_link->num_params = 1; + + return 0; +} + int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd) { struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_init_dai(rtd->codec_dai, + ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, 0), dai_props->codec_dai); if (ret < 0) return ret; - ret = asoc_simple_init_dai(rtd->cpu_dai, + ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, 0), dai_props->cpu_dai); if (ret < 0) return ret; + ret = asoc_simple_init_dai_link_params(rtd, dai_props); + if (ret < 0) + return ret; + return 0; } EXPORT_SYMBOL_GPL(asoc_simple_dai_init); diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index fdd2c73fd2fa..a495d1050d49 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -397,7 +397,7 @@ static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st, struct snd_dmaengine_dai_dma_data *dma_data; int ret; - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st); + dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), st); ret = snd_hwparams_to_dma_slave_config(st, params, sc); if (ret) diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index 4b1853409633..db052ec17d5d 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -403,7 +403,7 @@ static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st, struct snd_dmaengine_dai_dma_data *dma_data; int ret; - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st); + dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), st); ret = snd_hwparams_to_dma_slave_config(st, params, sc); if (ret) diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index baef461a99f1..f883c9340eee 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -1333,7 +1333,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) dai->capture_widget->name); w = dai->capture_widget; snd_soc_dapm_widget_for_each_source_path(w, p) { - if (p->connected && !p->connected(w, p->sink)) + if (p->connected && !p->connected(w, p->source)) continue; if (p->connect && p->source->power && diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 340bd2be39a7..82f2b6357778 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -649,7 +649,7 @@ static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component, static int sst_soc_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); struct snd_pcm *pcm = rtd->pcm; if (dai->driver->playback.channels_min || @@ -741,7 +741,7 @@ static int sst_soc_prepare(struct device *dev) /* set the SSPs to idle */ for_each_card_rtds(drv->soc_card, rtd) { - struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); if (dai->active) { send_ssp_cmd(dai, dai->name, 0); @@ -762,7 +762,7 @@ static void sst_soc_complete(struct device *dev) /* restart SSPs */ for_each_card_rtds(drv->soc_card, rtd) { - struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); if (dai->active) { sst_handle_vb_timer(dai, true); diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index d952719bc098..5862fe968083 100644 --- a/sound/soc/intel/atom/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c @@ -99,7 +99,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram); do_release_regions: pci_release_regions(pci); - return 0; + return ret; } /* diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 9ca2567d0059..556c3104e641 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -289,7 +289,6 @@ config SND_SOC_INTEL_DA7219_MAX98357A_GENERIC select SND_SOC_DA7219 select SND_SOC_MAX98357A select SND_SOC_DMIC - select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_HDAC_HDMI config SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON @@ -302,6 +301,7 @@ config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on SND_HDA_CODEC_HDMI select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON help This adds support for ASoC machine driver for Broxton-P platforms @@ -402,6 +402,7 @@ config SND_SOC_INTEL_GLK_DA7219_MAX98357A_MACH tristate "GLK with DA7219 and MAX98357A in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on SND_HDA_CODEC_HDMI select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON help This adds support for ASoC machine driver for Geminilake platforms @@ -413,10 +414,10 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH tristate "GLK with RT5682 and MAX98357A in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on SND_HDA_CODEC_HDMI select SND_SOC_RT5682 select SND_SOC_MAX98357A select SND_SOC_DMIC - select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_HDAC_HDMI help This adds support for ASoC machine driver for Geminilake platforms @@ -430,7 +431,7 @@ if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH tristate "SKL/KBL/BXT/APL with HDA Codecs" - select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC + depends on SND_HDA_CODEC_HDMI select SND_SOC_HDAC_HDMI select SND_SOC_DMIC # SND_SOC_HDAC_HDA is already selected @@ -448,15 +449,31 @@ config SND_SOC_INTEL_SOF_RT5682_MACH depends on I2C && ACPI depends on (SND_SOC_SOF_HDA_LINK && (MFD_INTEL_LPSS || COMPILE_TEST)) ||\ (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST)) + depends on SND_HDA_CODEC_HDMI + select SND_SOC_MAX98373 + select SND_SOC_RT1015 select SND_SOC_RT5682 select SND_SOC_DMIC - select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_HDAC_HDMI help This adds support for ASoC machine driver for SOF platforms with rt5682 codec. Say Y if you have such a device. If unsure select "N". + +config SND_SOC_INTEL_SOF_PCM512x_MACH + tristate "SOF with TI PCM512x codec" + depends on I2C && ACPI + depends on (SND_SOC_SOF_HDA_AUDIO_CODEC && (MFD_INTEL_LPSS || COMPILE_TEST)) ||\ + (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST)) + depends on SND_HDA_CODEC_HDMI + select SND_SOC_PCM512x_I2C + help + This adds support for ASoC machine driver for SOF platforms + with TI PCM512x I2S audio codec. + Say Y or m 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_LP && SND_SOC_SOF_HDA_LINK) @@ -476,11 +493,11 @@ config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH tristate "CML with RT1011 and RT5682 in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on SND_HDA_CODEC_HDMI select SND_SOC_RT1011 select SND_SOC_RT5682 select SND_SOC_DMIC select SND_SOC_HDAC_HDMI - select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC help This adds support for ASoC machine driver for SOF platform with RT1011 + RT5682 I2S codec. @@ -492,19 +509,43 @@ endif ## SND_SOC_SOF_COMETLAKE_LP && SND_SOC_SOF_HDA_LINK if SND_SOC_SOF_JASPERLAKE config SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH - tristate "SOF with DA7219 and MAX98373 in I2S Mode" + tristate "SOF with DA7219 and MAX98373/MAX98360A in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on SND_HDA_CODEC_HDMI select SND_SOC_DA7219 select SND_SOC_MAX98373 select SND_SOC_DMIC - select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC help This adds support for ASoC machine driver for SOF platforms - with DA7219 + MAX98373 I2S audio codec. + with DA7219 + MAX98373/MAX98360A I2S audio codec. Say Y if you have such a device. If unsure select "N". endif ## SND_SOC_SOF_JASPERLAKE +if SND_SOC_SOF_INTEL_SOUNDWIRE + +config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH + tristate "SoundWire generic machine driver" + depends on I2C && ACPI + depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES || COMPILE_TEST + depends on SOUNDWIRE + depends on SND_HDA_CODEC_HDMI + select SND_SOC_RT700_SDW + select SND_SOC_RT711_SDW + select SND_SOC_RT1308_SDW + select SND_SOC_RT1308 + select SND_SOC_RT715_SDW + select SND_SOC_RT5682_SDW + select SND_SOC_DMIC + help + Add support for Intel SoundWire-based platforms connected to + RT700, RT711, RT1308 and RT715 + If unsure select "N". + +endif + + endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index b74ddd49bd39..1ef6e60bc2a0 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,7 @@ snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o hda_dsp_common.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o hda_dsp_common.o +snd-soc-sst-sof-pcm512x-objs := sof_pcm512x.o hda_dsp_common.o snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o hda_dsp_common.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o @@ -18,7 +19,7 @@ snd-soc-sst-byt-cht-cx2072x-objs := bytcht_cx2072x.o snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o -snd-soc-sof_rt5682-objs := sof_rt5682.o hda_dsp_common.o +snd-soc-sof_rt5682-objs := sof_rt5682.o hda_dsp_common.o sof_maxim_common.o snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o hda_dsp_common.o snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o @@ -30,13 +31,18 @@ snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o hda_dsp_c snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o hda_dsp_common.o - +snd-soc-sof-sdw-objs += sof_sdw.o \ + sof_sdw_rt711.o sof_sdw_rt700.o \ + sof_sdw_rt1308.o sof_sdw_rt715.o \ + sof_sdw_rt5682.o \ + sof_sdw_dmic.o sof_sdw_hdmi.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.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 +obj-$(CONFIG_SND_SOC_INTEL_SOF_PCM512x_MACH) += snd-soc-sst-sof-pcm512x.o obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5650_MACH) += snd-soc-sst-bdw-rt5650-mach.o @@ -62,4 +68,4 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max9 obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o - +obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_SOF_MACH) += snd-soc-sof-sdw.o diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c index 1a302436d450..6c2fdb5659ed 100644 --- a/sound/soc/intel/boards/bdw-rt5650.c +++ b/sound/soc/intel/boards/bdw-rt5650.c @@ -107,7 +107,7 @@ static int bdw_rt5650_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; /* Workaround: set codec PLL to 19.2MHz that PLL source is @@ -166,8 +166,8 @@ static int bdw_rt5650_init(struct snd_soc_pcm_runtime *rtd) { struct bdw_rt5650_priv *bdw_rt5650 = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *component = rtd->codec_dai->component; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; int ret; /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1. @@ -226,9 +226,6 @@ SND_SOC_DAILINK_DEF(be, #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) SND_SOC_DAILINK_DEF(ssp0_port, DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); -#else -SND_SOC_DAILINK_DEF(ssp0_port, - DAILINK_COMP_ARRAY(COMP_DUMMY())); #endif static struct snd_soc_dai_link bdw_rt5650_dais[] = { @@ -264,7 +261,11 @@ static struct snd_soc_dai_link bdw_rt5650_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, .init = bdw_rt5650_init, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + SND_SOC_DAILINK_REG(dummy, be, dummy), +#else SND_SOC_DAILINK_REG(ssp0_port, be, platform), +#endif }, }; @@ -298,7 +299,7 @@ static int bdw_rt5650_probe(struct platform_device *pdev) return -ENOMEM; /* override plaform name, if required */ - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt5650_card, mach->mach_params.platform); diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index bb643c99069d..6b4b64098d36 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -157,7 +157,7 @@ static int bdw_rt5677_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, 24576000, @@ -174,7 +174,7 @@ static int bdw_rt5677_dsp_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_PLL1, 24576000, @@ -226,7 +226,7 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) { struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); int ret; @@ -298,9 +298,6 @@ SND_SOC_DAILINK_DEF(be, #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) SND_SOC_DAILINK_DEF(ssp0_port, DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); -#else -SND_SOC_DAILINK_DEF(ssp0_port, - DAILINK_COMP_ARRAY(COMP_DUMMY())); #endif /* Wake on voice interface */ @@ -350,7 +347,11 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, .init = bdw_rt5677_init, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + SND_SOC_DAILINK_REG(dummy, be, dummy), +#else SND_SOC_DAILINK_REG(ssp0_port, be, platform), +#endif }, }; @@ -412,7 +413,7 @@ static int bdw_rt5677_probe(struct platform_device *pdev) } /* override plaform name, if required */ - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt5677_card, mach->mach_params.platform); if (ret) diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index b9c12e24c70b..acb4e36682cb 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -70,7 +70,7 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = { static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret = 0; ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset, @@ -104,7 +104,7 @@ static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, @@ -167,9 +167,6 @@ SND_SOC_DAILINK_DEF(codec, #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) SND_SOC_DAILINK_DEF(ssp0_port, DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); -#else -SND_SOC_DAILINK_DEF(ssp0_port, - DAILINK_COMP_ARRAY(COMP_DUMMY())); #endif /* broadwell digital audio interface glue - connects codec <--> CPU */ @@ -226,7 +223,11 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .ops = &broadwell_rt286_ops, .dpcm_playback = 1, .dpcm_capture = 1, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + SND_SOC_DAILINK_REG(dummy, codec, dummy), +#else SND_SOC_DAILINK_REG(ssp0_port, codec, platform), +#endif }, }; @@ -283,7 +284,7 @@ static int broadwell_audio_probe(struct platform_device *pdev) broadwell_rt286.dev = &pdev->dev; /* override plaform name, if required */ - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; ret = snd_soc_fixup_dai_links_platform_name(&broadwell_rt286, mach->mach_params.platform); if (ret) diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 9177401c37a5..44016c16f25e 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -179,8 +179,8 @@ static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd, static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) { int ret; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int clk_freq; /* Configure sysclk for codec */ @@ -226,7 +226,7 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd) { struct bxt_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct bxt_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -244,7 +244,7 @@ static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd) static int broxton_da7219_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; dapm = snd_soc_component_get_dapm(component); snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); @@ -721,7 +721,7 @@ static int broxton_audio_probe(struct platform_device *pdev) } /* override plaform name, if required */ - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; ret = snd_soc_fixup_dai_links_platform_name(&broxton_audio_card, diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 4b67f261377c..7a4decf34191 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -155,7 +155,7 @@ static const struct snd_soc_dapm_route geminilake_rt298_map[] = { static int broxton_rt298_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; dapm = snd_soc_component_get_dapm(component); snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); @@ -165,7 +165,7 @@ static int broxton_rt298_fe_init(struct snd_soc_pcm_runtime *rtd) static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret = 0; ret = snd_soc_card_jack_new(rtd->card, "Headset", @@ -186,7 +186,7 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd) { struct bxt_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct bxt_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -225,7 +225,7 @@ static int broxton_rt298_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, @@ -627,7 +627,7 @@ static int broxton_audio_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, ctx); /* override plaform name, if required */ - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; ret = snd_soc_fixup_dai_links_platform_name(card, diff --git a/sound/soc/intel/boards/byt-max98090.c b/sound/soc/intel/boards/byt-max98090.c index 01739ad75b12..f5097da28828 100644 --- a/sound/soc/intel/boards/byt-max98090.c +++ b/sound/soc/intel/boards/byt-max98090.c @@ -89,7 +89,7 @@ static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime) card->dapm.idle_bias_off = true; - ret = snd_soc_dai_set_sysclk(runtime->codec_dai, + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(runtime, 0), M98090_REG_SYSTEM_CLOCK, 25000000, SND_SOC_CLOCK_IN); if (ret < 0) { diff --git a/sound/soc/intel/boards/byt-rt5640.c b/sound/soc/intel/boards/byt-rt5640.c index 0c76dafdd572..ace232f8aed6 100644 --- a/sound/soc/intel/boards/byt-rt5640.c +++ b/sound/soc/intel/boards/byt-rt5640.c @@ -73,7 +73,7 @@ static int byt_rt5640_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, @@ -123,7 +123,7 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_component *component = runtime->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; struct snd_soc_card *card = runtime->card; const struct snd_soc_dapm_route *custom_map; int num_routes; diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c index 67f06c95eec5..3b3df7c9008c 100644 --- a/sound/soc/intel/boards/bytcht_cx2072x.c +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -70,7 +70,7 @@ static const struct acpi_gpio_mapping byt_cht_cx2072x_acpi_gpios[] = { static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_component *codec = rtd->codec_dai->component; + struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component; int ret; if (devm_acpi_dev_add_driver_gpios(codec->dev, @@ -80,7 +80,7 @@ static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd) card->dapm.idle_bias_off = true; /* set the default PLL rate, the clock is handled by the codec driver */ - ret = snd_soc_dai_set_sysclk(rtd->codec_dai, CX2072X_MCLK_EXTERNAL_PLL, + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), CX2072X_MCLK_EXTERNAL_PLL, 19200000, SND_SOC_CLOCK_IN); if (ret) { dev_err(rtd->dev, "Could not set sysclk\n"); @@ -97,7 +97,7 @@ static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd) snd_soc_component_set_jack(codec, &byt_cht_cx2072x_headset, NULL); - snd_soc_dai_set_bclk_ratio(rtd->codec_dai, 50); + snd_soc_dai_set_bclk_ratio(asoc_rtd_to_codec(rtd, 0), 50); return ret; } @@ -123,7 +123,7 @@ static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd, * with explicit setting to I2S 2ch 24-bit. The word length is set with * dai_set_tdm_slot() since there is no other API exposed */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -132,7 +132,7 @@ static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24); if (ret < 0) { dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index eda7a500cad6..5e96e7d02733 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -78,7 +78,7 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd, * with explicit setting to I2S 2ch 24-bit. The word length is set with * dai_set_tdm_slot() since there is no other API exposed */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -87,7 +87,7 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24); if (ret < 0) { dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); return ret; @@ -106,7 +106,7 @@ static int aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK, @@ -127,7 +127,7 @@ static int aif1_hw_params(struct snd_pcm_substream *substream, static int aif1_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_pll(codec_dai, 0, @@ -231,7 +231,7 @@ static int bytcht_da7213_probe(struct platform_device *pdev) int ret_val = 0; int i; - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; card = &bytcht_da7213_card; card->dev = &pdev->dev; diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 0adc5a5e134a..ddcd070100ef 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -157,7 +157,7 @@ static struct snd_soc_jack_pin byt_cht_es8316_jack_pins[] = { static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) { - struct snd_soc_component *codec = runtime->codec_dai->component; + struct snd_soc_component *codec = asoc_rtd_to_codec(runtime, 0)->component; struct snd_soc_card *card = runtime->card; struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); const struct snd_soc_dapm_route *custom_map; @@ -212,7 +212,7 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) if (ret) dev_err(card->dev, "unable to enable MCLK\n"); - ret = snd_soc_dai_set_sysclk(runtime->codec_dai, 0, 19200000, + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(runtime, 0), 0, 19200000, SND_SOC_CLOCK_IN); if (ret < 0) { dev_err(card->dev, "can't set codec clock %d\n", ret); @@ -262,7 +262,7 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, * with explicit setting to I2S 2ch 24-bit. The word length is set with * dai_set_tdm_slot() since there is no other API exposed */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS @@ -272,7 +272,7 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, bits); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits); if (ret < 0) { dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c index 479af808ef43..8c0dab1f4030 100644 --- a/sound/soc/intel/boards/bytcht_nocodec.c +++ b/sound/soc/intel/boards/bytcht_nocodec.c @@ -58,7 +58,7 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd, * with explicit setting to I2S 2ch 24-bit. The word length is set with * dai_set_tdm_slot() since there is no other API exposed */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -68,7 +68,7 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24); if (ret < 0) { dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 6bd9ae813be2..33fb8ea4e5cb 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -381,7 +381,7 @@ static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); return byt_rt5640_prepare_and_enable_pll1(dai, params_rate(params)); } @@ -805,7 +805,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_component *component = runtime->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; const struct snd_soc_dapm_route *custom_map; int num_routes; int ret; @@ -962,7 +962,7 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, * with explicit setting to I2S 2ch. The word length is set with * dai_set_tdm_slot() since there is no other API exposed */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -971,7 +971,7 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, bits); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits); if (ret < 0) { dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 5074bb53f98e..214ef41e23e6 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -348,7 +348,7 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); snd_pcm_format_t format = params_format(params); int rate = params_rate(params); int bclk_ratio; @@ -540,7 +540,7 @@ static int byt_rt5651_add_codec_device_props(struct device *i2c_dev) static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; - struct snd_soc_component *codec = runtime->codec_dai->component; + struct snd_soc_component *codec = asoc_rtd_to_codec(runtime, 0)->component; struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); const struct snd_soc_dapm_route *custom_map; int num_routes; @@ -685,7 +685,7 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd, * with explicit setting to I2S 2ch. The word length is set with * dai_set_tdm_slot() since there is no other API exposed */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS @@ -696,7 +696,7 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, bits); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits); if (ret < 0) { dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 70bb86f3342f..135701738a44 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -113,7 +113,7 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, M98090_REG_SYSTEM_CLOCK, @@ -257,7 +257,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, int ret = 0; unsigned int fmt = 0; - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 16); if (ret < 0) { dev_err(rtd->dev, "can't set cpu_dai slot fmt: %d\n", ret); return ret; @@ -266,7 +266,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt); + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), fmt); if (ret < 0) { dev_err(rtd->dev, "can't set cpu_dai set fmt: %d\n", ret); return ret; @@ -553,7 +553,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* override plaform name, if required */ snd_soc_card_cht.dev = &pdev->dev; - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, diff --git a/sound/soc/intel/boards/cht_bsw_nau8824.c b/sound/soc/intel/boards/cht_bsw_nau8824.c index 501bad3976fb..f456150f89c2 100644 --- a/sound/soc/intel/boards/cht_bsw_nau8824.c +++ b/sound/soc/intel/boards/cht_bsw_nau8824.c @@ -73,7 +73,7 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, NAU8824_CLK_FLL_FS, 0, @@ -96,7 +96,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); struct snd_soc_jack *jack = &ctx->jack; - struct snd_soc_dai *codec_dai = runtime->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); struct snd_soc_component *component = codec_dai->component; int ret, jack_type; @@ -259,7 +259,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* override plaform name, if required */ snd_soc_card_cht.dev = &pdev->dev; - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index b5b016d493f1..e64eca56e426 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -208,7 +208,7 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; /* set codec PLL source to the 19.2MHz platform clock (MCLK) */ @@ -252,7 +252,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); - struct snd_soc_component *component = runtime->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; int jack_type; int ret; @@ -359,7 +359,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, * with explicit setting to I2S 2ch 16-bit. The word length is set with * dai_set_tdm_slot() since there is no other API exposed */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS @@ -369,7 +369,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - ret = snd_soc_dai_set_fmt(rtd->codec_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS @@ -379,7 +379,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 16); if (ret < 0) { dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); return ret; @@ -393,7 +393,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, /* * Default mode for SSP configuration is TDM 4 slot */ - ret = snd_soc_dai_set_fmt(rtd->codec_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -403,7 +403,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, } /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ - ret = snd_soc_dai_set_tdm_slot(rtd->codec_dai, 0xF, 0xF, 4, 24); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 0), 0xF, 0xF, 4, 24); if (ret < 0) { dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret); return ret; @@ -539,7 +539,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) if (!drv) return -ENOMEM; - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) { if (acpi_dev_found(snd_soc_cards[i].codec_id) && diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 9d657421730a..097023a3ec14 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -144,7 +144,7 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; /* set codec PLL source to the 19.2MHz platform clock (MCLK) */ @@ -176,7 +176,7 @@ static const struct acpi_gpio_mapping cht_rt5672_gpios[] = { static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_dai *codec_dai = runtime->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); struct snd_soc_component *component = codec_dai->component; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); @@ -255,7 +255,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, /* * Default mode for SSP configuration is TDM 4 slot */ - ret = snd_soc_dai_set_fmt(rtd->codec_dai, + ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -265,7 +265,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, } /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ - ret = snd_soc_dai_set_tdm_slot(rtd->codec_dai, 0xF, 0xF, 4, 24); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 0), 0xF, 0xF, 4, 24); if (ret < 0) { dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/cml_rt1011_rt5682.c b/sound/soc/intel/boards/cml_rt1011_rt5682.c index dd80d0186a6c..8167b2977e1d 100644 --- a/sound/soc/intel/boards/cml_rt1011_rt5682.c +++ b/sound/soc/intel/boards/cml_rt1011_rt5682.c @@ -85,7 +85,7 @@ static const struct snd_soc_dapm_route cml_rt1011_rt5682_map[] = { static int cml_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) { struct card_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_jack *jack; int ret; @@ -125,7 +125,7 @@ static int cml_rt5682_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int clk_id, clk_freq, pll_out, ret; clk_id = RT5682_PLL1_S_MCLK; @@ -164,8 +164,7 @@ static int cml_rt1011_hw_params(struct snd_pcm_substream *substream, srate = params_rate(params); - for (i = 0; i < rtd->num_codecs; i++) { - codec_dai = rtd->codec_dais[i]; + for_each_rtd_codec_dais(rtd, i, codec_dai) { /* 100 Fs to drive 24 bit data */ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK, @@ -275,7 +274,7 @@ static int sof_card_late_probe(struct snd_soc_card *card) static int hdmi_init(struct snd_soc_pcm_runtime *rtd) { struct card_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -447,12 +446,12 @@ static int snd_cml_rt1011_probe(struct platform_device *pdev) const char *platform_name; int ret; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; INIT_LIST_HEAD(&ctx->hdmi_pcm_list); - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; snd_soc_card_cml.dev = &pdev->dev; platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index 8e947bad143c..f13158e4a1fc 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -136,8 +136,8 @@ static int geminilake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) { struct glk_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *component = rtd->codec_dai->component; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_jack *jack; int ret; @@ -188,7 +188,7 @@ static int geminilake_rt5682_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; /* Set valid bitmask & configuration for I2S in 24 bit */ @@ -208,7 +208,7 @@ static struct snd_soc_ops geminilake_rt5682_ops = { static int geminilake_hdmi_init(struct snd_soc_pcm_runtime *rtd) { struct glk_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct glk_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -225,7 +225,7 @@ static int geminilake_hdmi_init(struct snd_soc_pcm_runtime *rtd) static int geminilake_rt5682_fe_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; struct snd_soc_dapm_context *dapm; int ret; @@ -409,6 +409,7 @@ static struct snd_soc_dai_link geminilake_dais[] = { .init = NULL, .capture_only = 1, .nonatomic = 1, + .dynamic = 1, SND_SOC_DAILINK_REG(echoref, dummy, platform), }, [GLK_DPCM_AUDIO_REF_CP] = { @@ -604,7 +605,7 @@ static int geminilake_audio_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, ctx); /* override plaform name, if required */ - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index 3dadf9bff796..3ed53d7db4e6 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -56,7 +56,7 @@ static int haswell_rt5640_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000, @@ -193,7 +193,7 @@ static int haswell_audio_probe(struct platform_device *pdev) haswell_rt5640.dev = &pdev->dev; /* override plaform name, if required */ - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; ret = snd_soc_fixup_dai_links_platform_name(&haswell_rt5640, mach->mach_params.platform); if (ret) diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index bc7f9a9ce9af..32cd90b8d4c4 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -159,8 +159,8 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) { struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *component = rtd->codec_dai->component; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_jack *jack; int ret; @@ -203,7 +203,7 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device) { struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct kbl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -236,7 +236,7 @@ static int kabylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) static int kabylake_da7219_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; dapm = snd_soc_component_get_dapm(component); snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index 7a13e9b35187..abd4e3839678 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -176,10 +176,10 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *runtime = substream->private_data; + struct snd_soc_dai *codec_dai; int ret, j; - for (j = 0; j < runtime->num_codecs; j++) { - struct snd_soc_dai *codec_dai = runtime->codec_dais[j]; + for_each_rtd_codec_dais(runtime, j, codec_dai) { if (!strcmp(codec_dai->component->name, MAX98927_DEV0_NAME)) { ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16); @@ -221,10 +221,10 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, static int kabylake_ssp0_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; int j, ret; - for (j = 0; j < rtd->num_codecs; j++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + for_each_rtd_codec_dais(rtd, j, codec_dai) { const char *name = codec_dai->component->name; struct snd_soc_component *component = codec_dai->component; struct snd_soc_dapm_context *dapm = @@ -331,7 +331,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) { struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_jack *jack; struct snd_soc_card *card = rtd->card; int ret; @@ -381,7 +381,7 @@ static int kabylake_dmic_init(struct snd_soc_pcm_runtime *rtd) static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device) { struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct kbl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -414,7 +414,7 @@ static int kabylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) static int kabylake_da7219_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; dapm = snd_soc_component_get_dapm(component); snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c index e23dea9ab79a..6460e3f0c974 100644 --- a/sound/soc/intel/boards/kbl_rt5660.c +++ b/sound/soc/intel/boards/kbl_rt5660.c @@ -157,7 +157,7 @@ static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd) { int ret; struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); ret = devm_acpi_dev_add_driver_gpios(component->dev, acpi_rt5660_gpios); @@ -210,7 +210,7 @@ static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd) static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device) { struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct kbl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -244,7 +244,7 @@ static int kabylake_rt5660_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index d8f2ff7139a9..658a9da3a40f 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -242,7 +242,7 @@ static int kabylake_rt5663_fe_init(struct snd_soc_pcm_runtime *rtd) { int ret; struct snd_soc_dapm_context *dapm; - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; dapm = snd_soc_component_get_dapm(component); ret = snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); @@ -258,7 +258,7 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) { int ret; struct kbl_rt5663_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_jack *jack; /* @@ -305,7 +305,7 @@ static int kabylake_rt5663_max98927_codec_init(struct snd_soc_pcm_runtime *rtd) static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device) { struct kbl_rt5663_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct kbl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -431,7 +431,7 @@ static int kabylake_rt5663_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; /* use ASRC for internal clocks, as PLL rate isn't multiple of BCLK */ @@ -472,7 +472,7 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; int ret = 0, j; - for_each_rtd_codec_dai(rtd, j, codec_dai) { + for_each_rtd_codec_dais(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME)) { /* * Use channel 4 and 5 for the first amp @@ -962,7 +962,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) kabylake_audio_card->dev = &pdev->dev; snd_soc_card_set_drvdata(kabylake_audio_card, ctx); - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; if (mach) dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 96c814f36458..1b1f8d7a4ea3 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -206,7 +206,7 @@ static struct snd_soc_codec_conf max98927_codec_conf[] = { static int kabylake_rt5663_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; int ret; dapm = snd_soc_component_get_dapm(component); @@ -221,7 +221,7 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) { int ret; struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_jack *jack; /* @@ -255,7 +255,7 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device) { struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct kbl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -372,7 +372,7 @@ static int kabylake_rt5663_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; /* use ASRC for internal clocks, as PLL rate isn't multiple of BCLK */ @@ -399,7 +399,7 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; int ret = 0, j; - for_each_rtd_codec_dai(rtd, j, codec_dai) { + for_each_rtd_codec_dais(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name, RT5514_DEV_NAME)) { ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0, 8, 16); if (ret < 0) { @@ -772,7 +772,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) kabylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&kabylake_audio_card, ctx); - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; if (mach) dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h index d6150670ca05..e8545d13062f 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.h +++ b/sound/soc/intel/boards/skl_hda_dsp_common.h @@ -49,6 +49,10 @@ static inline int skl_hda_hdmi_build_controls(struct snd_soc_card *card) struct snd_soc_component *component; struct skl_hda_hdmi_pcm *pcm; + /* HDMI disabled, do not create controls */ + if (list_empty(&ctx->hdmi_pcm_list)) + return 0; + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct skl_hda_hdmi_pcm, head); component = pcm->codec_dai->component; diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 11eaee9ae41f..3be764299ab0 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -61,6 +61,9 @@ static const struct snd_soc_dapm_route skl_hda_map[] = { { "Alt Analog CPU Capture", NULL, "Alt Analog Codec Capture" }, }; +SND_SOC_DAILINK_DEF(dummy_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai"))); + static int skl_hda_card_late_probe(struct snd_soc_card *card) { return skl_hda_hdmi_jack_init(card); @@ -114,13 +117,19 @@ static int skl_hda_fill_card_info(struct snd_soc_acpi_mach_params *mach_params) { struct snd_soc_card *card = &hda_soc_card; struct snd_soc_dai_link *dai_link; - u32 codec_count, codec_mask; + u32 codec_count, codec_mask, idisp_mask; int i, num_links, num_route; codec_mask = mach_params->codec_mask; codec_count = hweight_long(codec_mask); + idisp_mask = codec_mask & IDISP_CODEC_MASK; + + if (!codec_count || codec_count > 2 || + (codec_count == 2 && !idisp_mask)) + return -EINVAL; - if (codec_count == 1 && codec_mask & IDISP_CODEC_MASK) { + if (codec_mask == idisp_mask) { + /* topology with iDisp as the only HDA codec */ num_links = IDISP_DAI_COUNT + DMIC_DAI_COUNT; num_route = IDISP_ROUTE_COUNT; @@ -135,13 +144,19 @@ static int skl_hda_fill_card_info(struct snd_soc_acpi_mach_params *mach_params) skl_hda_be_dai_links[IDISP_DAI_COUNT + HDAC_DAI_COUNT + i]; } - } else if (codec_count == 2 && codec_mask & IDISP_CODEC_MASK) { + } else { + /* topology with external and iDisp HDA codecs */ num_links = ARRAY_SIZE(skl_hda_be_dai_links); num_route = ARRAY_SIZE(skl_hda_map); card->dapm_widgets = skl_hda_widgets; card->num_dapm_widgets = ARRAY_SIZE(skl_hda_widgets); - } else { - return -EINVAL; + if (!idisp_mask) { + for (i = 0; i < IDISP_DAI_COUNT; i++) { + skl_hda_be_dai_links[i].codecs = dummy_codec; + skl_hda_be_dai_links[i].num_codecs = + ARRAY_SIZE(dummy_codec); + } + } } card->num_links = num_links; @@ -167,7 +182,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ctx->hdmi_pcm_list); - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; if (!mach) return -EINVAL; diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index e6de3b28d840..d7b8154c43a4 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -157,7 +157,7 @@ static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) { int ret; - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; /* * Headset buttons map to the google Reference headset. @@ -182,7 +182,7 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) { struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct skl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -200,7 +200,7 @@ static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) { struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct skl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -218,7 +218,7 @@ static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) { struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct skl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -236,7 +236,7 @@ static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; dapm = snd_soc_component_get_dapm(component); snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); @@ -296,7 +296,7 @@ static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, @@ -660,7 +660,7 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; if (mach) dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index c99c8b23e509..4b317bcf6ea0 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -161,12 +161,12 @@ static int skylake_ssm4567_codec_init(struct snd_soc_pcm_runtime *rtd) int ret; /* Slot 1 for left */ - ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[0], 0x01, 0x01, 2, 48); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 0), 0x01, 0x01, 2, 48); if (ret < 0) return ret; /* Slot 2 for right */ - ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[1], 0x02, 0x02, 2, 48); + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 1), 0x02, 0x02, 2, 48); if (ret < 0) return ret; @@ -176,7 +176,7 @@ static int skylake_ssm4567_codec_init(struct snd_soc_pcm_runtime *rtd) static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) { int ret; - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; /* * 4 buttons here map to the google Reference headset @@ -201,7 +201,7 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) { struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct skl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -219,7 +219,7 @@ static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) { struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct skl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -238,7 +238,7 @@ static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) { struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct skl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -256,7 +256,7 @@ static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; dapm = snd_soc_component_get_dapm(component); snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); @@ -348,7 +348,7 @@ static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, @@ -686,6 +686,7 @@ static struct snd_soc_card skylake_audio_card = { .codec_conf = ssm4567_codec_conf, .num_configs = ARRAY_SIZE(ssm4567_codec_conf), .fully_routed = true, + .disable_route_checks = true, .late_probe = skylake_card_late_probe, }; @@ -703,7 +704,7 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; if (mach) dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index a9aec66a2351..903ae1b28ec9 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -112,7 +112,7 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = { static int skylake_rt286_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; - struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_component *component = asoc_rtd_to_cpu(rtd, 0)->component; dapm = snd_soc_component_get_dapm(component); snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); @@ -122,7 +122,7 @@ static int skylake_rt286_fe_init(struct snd_soc_pcm_runtime *rtd) static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret; ret = snd_soc_card_jack_new(rtd->card, "Headset", @@ -143,7 +143,7 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd) { struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct skl_hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -229,7 +229,7 @@ static int skylake_rt286_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, diff --git a/sound/soc/intel/boards/sof_da7219_max98373.c b/sound/soc/intel/boards/sof_da7219_max98373.c index 8f44f13d2848..b707dd3b5625 100644 --- a/sound/soc/intel/boards/sof_da7219_max98373.c +++ b/sound/soc/intel/boards/sof_da7219_max98373.c @@ -2,7 +2,7 @@ // Copyright(c) 2019 Intel Corporation. /* - * Intel SOF Machine driver for DA7219 + MAX98373 codec + * Intel SOF Machine driver for DA7219 + MAX98373/MAX98360A codec */ #include <linux/input.h> @@ -69,11 +69,20 @@ static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Right Spk"), }; +static const struct snd_kcontrol_new m98360a_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +/* For MAX98373 amp */ static const struct snd_soc_dapm_widget 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), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), @@ -83,21 +92,45 @@ static const struct snd_soc_dapm_route audio_map[] = { { "Headphone Jack", NULL, "HPL" }, { "Headphone Jack", NULL, "HPR" }, + { "MIC", NULL, "Headset Mic" }, + + { "Headphone Jack", NULL, "Platform Clock" }, + { "Headset Mic", NULL, "Platform Clock" }, + { "Left Spk", NULL, "Left BE_OUT" }, { "Right Spk", NULL, "Right BE_OUT" }, +}; + +/* For MAX98360A amp */ +static const struct snd_soc_dapm_widget max98360a_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + + SND_SOC_DAPM_SPK("Spk", NULL), + + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU), +}; + +static const struct snd_soc_dapm_route max98360a_map[] = { + { "Headphone Jack", NULL, "HPL" }, + { "Headphone Jack", NULL, "HPR" }, { "MIC", NULL, "Headset Mic" }, { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + + {"Spk", NULL, "Speaker"}, }; static struct snd_soc_jack headset; static int da7219_codec_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; struct snd_soc_jack *jack; int ret; @@ -140,7 +173,7 @@ static int ssp1_hw_params(struct snd_pcm_substream *substream, int ret, j; for (j = 0; j < runtime->num_codecs; j++) { - struct snd_soc_dai *codec_dai = runtime->codec_dais[j]; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, j); if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME)) { /* vmon_slot_no = 0 imon_slot_no = 1 for TX slots */ @@ -181,7 +214,7 @@ static struct snd_soc_codec_conf max98373_codec_conf[] = { static int hdmi_init(struct snd_soc_pcm_runtime *rtd) { struct card_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); @@ -224,6 +257,9 @@ SND_SOC_DAILINK_DEF(ssp1_amps, /* Left */ COMP_CODEC(MAXIM_DEV0_NAME, MAX98373_CODEC_DAI), /* Right */ COMP_CODEC(MAXIM_DEV1_NAME, MAX98373_CODEC_DAI))); +SND_SOC_DAILINK_DEF(ssp1_m98360a, + DAILINK_COMP_ARRAY(COMP_CODEC("MX98360A:00", "HiFi"))); + SND_SOC_DAILINK_DEF(dmic_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin"))); SND_SOC_DAILINK_DEF(dmic_codec, @@ -320,6 +356,21 @@ static struct snd_soc_card card_da7219_m98373 = { .late_probe = card_late_probe, }; +static struct snd_soc_card card_da7219_m98360a = { + .name = "da7219max98360a", + .owner = THIS_MODULE, + .dai_link = dais, + .num_links = ARRAY_SIZE(dais), + .controls = m98360a_controls, + .num_controls = ARRAY_SIZE(m98360a_controls), + .dapm_widgets = max98360a_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98360a_widgets), + .dapm_routes = max98360a_map, + .num_dapm_routes = ARRAY_SIZE(max98360a_map), + .fully_routed = true, + .late_probe = card_late_probe, +}; + static int audio_probe(struct platform_device *pdev) { static struct snd_soc_card *card; @@ -327,15 +378,26 @@ static int audio_probe(struct platform_device *pdev) struct card_private *ctx; int ret; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; + /* By default dais[0] is configured for max98373 */ + if (!strcmp(pdev->name, "sof_da7219_max98360a")) { + dais[0] = (struct snd_soc_dai_link) { + .name = "SSP1-Codec", + .id = 0, + .no_pcm = 1, + .dpcm_playback = 1, + .ignore_pmdown_time = 1, + SND_SOC_DAILINK_REG(ssp1_pin, ssp1_m98360a, platform) }; + } + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); card = (struct snd_soc_card *)pdev->id_entry->driver_data; card->dev = &pdev->dev; - mach = (&pdev->dev)->platform_data; + mach = pdev->dev.platform_data; ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform); if (ret) @@ -351,13 +413,17 @@ static const struct platform_device_id board_ids[] = { .name = "sof_da7219_max98373", .driver_data = (kernel_ulong_t)&card_da7219_m98373, }, + { + .name = "sof_da7219_max98360a", + .driver_data = (kernel_ulong_t)&card_da7219_m98360a, + }, { } }; static struct platform_driver audio = { .probe = audio_probe, .driver = { - .name = "sof_da7219_max98373", + .name = "sof_da7219_max98_360a_373", .pm = &snd_soc_pm_ops, }, .id_table = board_ids, @@ -368,4 +434,5 @@ module_platform_driver(audio) MODULE_DESCRIPTION("ASoC Intel(R) SOF Machine driver"); MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sof_da7219_max98360a"); MODULE_ALIAS("platform:sof_da7219_max98373"); diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c new file mode 100644 index 000000000000..463b39a7ccfd --- /dev/null +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +#include <linux/string.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/soc-dapm.h> +#include <uapi/sound/asound.h> +#include "sof_maxim_common.h" + +static const struct snd_soc_dapm_route max_98373_dapm_routes[] = { + /* speaker */ + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, +}; + +static struct snd_soc_codec_conf max_98373_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAX_98373_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98373_DEV1_NAME), + .name_prefix = "Left", + }, +}; + +struct snd_soc_dai_link_component max_98373_components[] = { + { /* For Left */ + .name = MAX_98373_DEV0_NAME, + .dai_name = MAX_98373_CODEC_DAI, + }, + { /* For Right */ + .name = MAX_98373_DEV1_NAME, + .dai_name = MAX_98373_CODEC_DAI, + }, +}; + +static int max98373_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; + int j; + + for_each_rtd_codec_dais(rtd, j, codec_dai) { + if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) { + /* DEV0 tdm slot configuration */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16); + } + if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { + /* DEV1 tdm slot configuration */ + snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16); + } + } + return 0; +} + +struct snd_soc_ops max_98373_ops = { + .hw_params = max98373_hw_params, +}; + +int max98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_add_routes(&card->dapm, max_98373_dapm_routes, + ARRAY_SIZE(max_98373_dapm_routes)); + if (ret) + dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret); + return ret; +} + +void sof_max98373_codec_conf(struct snd_soc_card *card) +{ + card->codec_conf = max_98373_codec_conf; + card->num_configs = ARRAY_SIZE(max_98373_codec_conf); +} diff --git a/sound/soc/intel/boards/sof_maxim_common.h b/sound/soc/intel/boards/sof_maxim_common.h new file mode 100644 index 000000000000..406bf0e81155 --- /dev/null +++ b/sound/soc/intel/boards/sof_maxim_common.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2020 Intel Corporation. + */ + +/* + * This file defines data structures used in Machine Driver for Intel + * platforms with Maxim Codecs. + */ +#ifndef __SOF_MAXIM_COMMON_H +#define __SOF_MAXIM_COMMON_H + +#include <sound/soc.h> + +#define MAX_98373_CODEC_DAI "max98373-aif1" +#define MAX_98373_DEV0_NAME "i2c-MX98373:00" +#define MAX_98373_DEV1_NAME "i2c-MX98373:01" + +extern struct snd_soc_dai_link_component max_98373_components[2]; +extern struct snd_soc_ops max_98373_ops; + +int max98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd); +void sof_max98373_codec_conf(struct snd_soc_card *card); +#endif /* __SOF_MAXIM_COMMON_H */ diff --git a/sound/soc/intel/boards/sof_pcm512x.c b/sound/soc/intel/boards/sof_pcm512x.c new file mode 100644 index 000000000000..fb7811899999 --- /dev/null +++ b/sound/soc/intel/boards/sof_pcm512x.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018-2020 Intel Corporation. + +/* + * Intel SOF Machine Driver for Intel platforms with TI PCM512x codec, + * e.g. Up or Up2 with Hifiberry DAC+ HAT + */ +#include <linux/clk.h> +#include <linux/dmi.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "../../codecs/pcm512x.h" +#include "../common/soc-intel-quirks.h" +#include "hda_dsp_common.h" + +#define NAME_SIZE 32 + +#define SOF_PCM512X_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0)) +#define SOF_PCM512X_SSP_CODEC_MASK (GENMASK(3, 0)) + +#define IDISP_CODEC_MASK 0x4 + +/* Default: SSP5 */ +static unsigned long sof_pcm512x_quirk = SOF_PCM512X_SSP_CODEC(5); + +static bool is_legacy_cpu; + +struct sof_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct sof_card_private { + struct list_head hdmi_pcm_list; + bool idisp_codec; +}; + +static int sof_pcm512x_quirk_cb(const struct dmi_system_id *id) +{ + sof_pcm512x_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id sof_pcm512x_quirk_table[] = { + { + .callback = sof_pcm512x_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), + DMI_MATCH(DMI_PRODUCT_NAME, "UP-CHT01"), + }, + .driver_data = (void *)(SOF_PCM512X_SSP_CODEC(2)), + }, + {} +}; + +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_pcm512x_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component; + + snd_soc_component_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x08); + + return 0; +} + +static int aif1_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component; + + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x08); + + return 0; +} + +static void aif1_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component; + + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x00); +} + +static const struct snd_soc_ops sof_pcm512x_ops = { + .startup = aif1_startup, + .shutdown = aif1_shutdown, +}; + +static struct snd_soc_dai_link_component platform_component[] = { + { + /* name might be overridden during probe */ + .name = "0000:00:1f.3" + } +}; + +#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) +static int sof_card_late_probe(struct snd_soc_card *card) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); + struct sof_hdmi_pcm *pcm; + + /* HDMI is not supported by SOF on Baytrail/CherryTrail */ + if (is_legacy_cpu) + return 0; + + if (list_empty(&ctx->hdmi_pcm_list)) + return -EINVAL; + + if (!ctx->idisp_codec) + return 0; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head); + + return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component); +} +#else +static int sof_card_late_probe(struct snd_soc_card *card) +{ + return 0; +} +#endif + +static const struct snd_kcontrol_new sof_controls[] = { + SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static const struct snd_soc_dapm_widget sof_widgets[] = { + SND_SOC_DAPM_SPK("Ext 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[] = { + /* Speaker */ + {"Ext Spk", NULL, "OUTR"}, + {"Ext Spk", NULL, "OUTL"}, +}; + +static const struct snd_soc_dapm_route dmic_map[] = { + /* digital mics */ + {"DMic", NULL, "SoC DMIC"}, +}; + +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 pcm512x codec */ +static struct snd_soc_card sof_audio_card_pcm512x = { + .name = "pcm512x", + .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, +}; + +SND_SOC_DAILINK_DEF(pcm512x_component, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-104C5122:00", "pcm512x-hifi"))); +SND_SOC_DAILINK_DEF(dmic_component, + DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); + +static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, + int ssp_codec, + int dmic_be_num, + int hdmi_num, + bool idisp_codec) +{ + 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_kcalloc(dev, sof_audio_card_pcm512x.num_links, + sizeof(struct snd_soc_dai_link), GFP_KERNEL); + cpus = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links, + sizeof(struct snd_soc_dai_link_component), 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 = pcm512x_component; + links[id].num_codecs = ARRAY_SIZE(pcm512x_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].init = sof_pcm512x_codec_init; + links[id].ops = &sof_pcm512x_ops; + links[id].nonatomic = true; + links[id].dpcm_playback = 1; + /* + * capture only supported with specific versions of the Hifiberry DAC+ + * links[id].dpcm_capture = 1; + */ + links[id].no_pcm = 1; + links[id].cpus = &cpus[id]; + links[id].num_cpus = 1; + if (is_legacy_cpu) { + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "ssp%d-port", + ssp_codec); + if (!links[id].cpus->dai_name) + goto devm_err; + } else { + 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_kcalloc(dev, hdmi_num, + sizeof(struct snd_soc_dai_link_component), + 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; + + /* + * topology cannot be loaded if codec is missing, so + * use the dummy codec if needed + */ + if (idisp_codec) { + idisp_components[i - 1].name = "ehdaudio0D2"; + idisp_components[i - 1].dai_name = + devm_kasprintf(dev, GFP_KERNEL, + "intel-hdmi-hifi%d", i); + } else { + idisp_components[i - 1].name = "snd-soc-dummy"; + idisp_components[i - 1].dai_name = "snd-soc-dummy-dai"; + } + 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++; + } + + return links; +devm_err: + return NULL; +} + +static int sof_audio_probe(struct platform_device *pdev) +{ + struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; + struct snd_soc_dai_link *dai_links; + struct sof_card_private *ctx; + int dmic_be_num, hdmi_num; + int ret, ssp_codec; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + hdmi_num = 0; + if (soc_intel_is_byt() || soc_intel_is_cht()) { + is_legacy_cpu = true; + dmic_be_num = 0; + /* default quirk for legacy cpu */ + sof_pcm512x_quirk = SOF_PCM512X_SSP_CODEC(2); + } else { + dmic_be_num = 2; +#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) + if (mach->mach_params.common_hdmi_codec_drv && + (mach->mach_params.codec_mask & IDISP_CODEC_MASK)) + ctx->idisp_codec = true; + + /* links are always present in topology */ + hdmi_num = 3; +#endif + } + + dmi_check_system(sof_pcm512x_quirk_table); + + dev_dbg(&pdev->dev, "sof_pcm512x_quirk = %lx\n", sof_pcm512x_quirk); + + ssp_codec = sof_pcm512x_quirk & SOF_PCM512X_SSP_CODEC_MASK; + + /* compute number of dai links */ + sof_audio_card_pcm512x.num_links = 1 + dmic_be_num + hdmi_num; + + dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, + dmic_be_num, hdmi_num, + ctx->idisp_codec); + if (!dai_links) + return -ENOMEM; + + sof_audio_card_pcm512x.dai_link = dai_links; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + sof_audio_card_pcm512x.dev = &pdev->dev; + + /* set platform name for each dailink */ + ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_pcm512x, + mach->mach_params.platform); + if (ret) + return ret; + + snd_soc_card_set_drvdata(&sof_audio_card_pcm512x, ctx); + + return devm_snd_soc_register_card(&pdev->dev, + &sof_audio_card_pcm512x); +} + +static int sof_pcm512x_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_component *component = NULL; + + for_each_card_components(card, component) { + if (!strcmp(component->name, pcm512x_component[0].name)) { + snd_soc_component_set_jack(component, NULL, NULL); + break; + } + } + + return 0; +} + +static struct platform_driver sof_audio = { + .probe = sof_audio_probe, + .remove = sof_pcm512x_remove, + .driver = { + .name = "sof_pcm512x", + .pm = &snd_soc_pm_ops, + }, +}; +module_platform_driver(sof_audio) + +MODULE_DESCRIPTION("ASoC Intel(R) SOF + PCM512x Machine driver"); +MODULE_AUTHOR("Pierre-Louis Bossart"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sof_pcm512x"); diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 5d878873a8e0..8c29431b5847 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright(c) 2019 Intel Corporation. +// Copyright(c) 2019-2020 Intel Corporation. /* * Intel SOF Machine Driver with Realtek rt5682 Codec - * and speaker codec MAX98357A + * and speaker codec MAX98357A or RT1015. */ #include <linux/i2c.h> #include <linux/input.h> @@ -18,10 +18,12 @@ #include <sound/soc.h> #include <sound/rt5682.h> #include <sound/soc-acpi.h> +#include "../../codecs/rt1015.h" #include "../../codecs/rt5682.h" #include "../../codecs/hdac_hdmi.h" #include "../common/soc-intel-quirks.h" #include "hda_dsp_common.h" +#include "sof_maxim_common.h" #define NAME_SIZE 32 @@ -39,6 +41,8 @@ #define SOF_RT5682_NUM_HDMIDEV_MASK (GENMASK(12, 10)) #define SOF_RT5682_NUM_HDMIDEV(quirk) \ ((quirk << SOF_RT5682_NUM_HDMIDEV_SHIFT) & SOF_RT5682_NUM_HDMIDEV_MASK) +#define SOF_RT1015_SPEAKER_AMP_PRESENT BIT(13) +#define SOF_MAX98373_SPEAKER_AMP_PRESENT BIT(14) /* Default: MCLK on, MCLK 19.2M, SSP0 */ static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN | @@ -120,7 +124,7 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { 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 = rtd->codec_dai; + 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); @@ -139,7 +143,7 @@ static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd) static int sof_rt5682_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 = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_jack *jack; int ret; @@ -207,7 +211,7 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int clk_id, clk_freq, pll_out, ret; if (sof_rt5682_quirk & SOF_RT5682_MCLK_EN) { @@ -260,6 +264,42 @@ 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 = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai; + int i, ret; + + if (!snd_soc_card_get_codec_dai(card, "rt1015-aif")) + return 0; + + 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) * 50, + 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; + } + } + + 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 */ @@ -316,12 +356,17 @@ static const struct snd_kcontrol_new sof_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Spk"), + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right 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("Spk", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), }; static const struct snd_soc_dapm_widget dmic_widgets[] = { @@ -342,11 +387,22 @@ static const struct snd_soc_dapm_route speaker_map[] = { { "Spk", NULL, "Speaker" }, }; +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 speaker_codec_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -382,6 +438,17 @@ 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 */ @@ -417,6 +484,17 @@ static struct snd_soc_dai_link_component max98357a_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 *sof_card_dai_links_create(struct device *dev, int ssp_codec, int ssp_amp, @@ -556,11 +634,24 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, goto devm_err; links[id].id = id; - links[id].codecs = max98357a_component; - links[id].num_codecs = ARRAY_SIZE(max98357a_component); + 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; + } else if (sof_rt5682_quirk & + SOF_MAX98373_SPEAKER_AMP_PRESENT) { + links[id].codecs = max_98373_components; + links[id].num_codecs = ARRAY_SIZE(max_98373_components); + links[id].init = max98373_spk_codec_init; + links[id].ops = &max_98373_ops; + } else { + links[id].codecs = max98357a_component; + links[id].num_codecs = ARRAY_SIZE(max98357a_component); + links[id].init = speaker_codec_init; + } links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); - links[id].init = speaker_codec_init, links[id].nonatomic = true; links[id].dpcm_playback = 1; links[id].no_pcm = 1; @@ -604,7 +695,7 @@ static int sof_audio_probe(struct platform_device *pdev) dmi_check_system(sof_rt5682_quirk_table); - mach = (&pdev->dev)->platform_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. @@ -662,6 +753,9 @@ static int sof_audio_probe(struct platform_device *pdev) if (sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT) sof_audio_card_rt5682.num_links++; + if (sof_rt5682_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT) + sof_max98373_codec_conf(&sof_audio_card_rt5682); + dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp, dmic_be_num, hdmi_num); if (!dai_links) @@ -669,6 +763,11 @@ 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); + } + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); sof_audio_card_rt5682.dev = &pdev->dev; @@ -714,6 +813,24 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(1) | SOF_RT5682_NUM_HDMIDEV(4)), }, + { + .name = "jsl_rt5682_rt1015", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_MCLK_24MHZ | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT1015_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1)), + }, + { + .name = "tgl_max98373_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98373_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4)), + }, { } }; @@ -735,3 +852,5 @@ MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:sof_rt5682"); MODULE_ALIAS("platform:tgl_max98357a_rt5682"); +MODULE_ALIAS("platform:jsl_rt5682_rt1015"); +MODULE_ALIAS("platform:tgl_max98373_rt5682"); diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c new file mode 100644 index 000000000000..a64dc563b47e --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw.c @@ -0,0 +1,962 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw - ASOC Machine driver for Intel SoundWire platforms + */ + +#include <linux/device.h> +#include <linux/dmi.h> +#include <linux/module.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "sof_sdw_common.h" + +unsigned long sof_sdw_quirk = SOF_RT711_JD_SRC_JD1; + +#define INC_ID(BE, CPU, LINK) do { (BE)++; (CPU)++; (LINK)++; } while (0) + +static int sof_sdw_quirk_cb(const struct dmi_system_id *id) +{ + sof_sdw_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id sof_sdw_quirk_table[] = { + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 | + SOF_RT715_DAI_ID_FIX), + }, + { + /* early version of SKU 09C6 */ + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 | + SOF_RT715_DAI_ID_FIX), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 | + SOF_RT715_DAI_ID_FIX | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 | + SOF_RT715_DAI_ID_FIX | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, + "Tiger Lake Client Platform"), + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD1 | + SOF_SDW_TGL_HDMI | SOF_SDW_PCH_DMIC | + SOF_SSP_PORT(SOF_I2S_SSP2)), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"), + }, + .driver_data = (void *)SOF_SDW_PCH_DMIC, + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "CometLake Client"), + }, + .driver_data = (void *)SOF_SDW_PCH_DMIC, + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_PRODUCT_NAME, "Volteer"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | SOF_SDW_PCH_DMIC), + }, + + {} +}; + +static struct snd_soc_codec_conf codec_conf[] = { + { + .dlc = COMP_CODEC_CONF("sdw:0:25d:711:0"), + .name_prefix = "rt711", + }, + /* rt1308 w/ I2S connection */ + { + .dlc = COMP_CODEC_CONF("i2c-10EC1308:00"), + .name_prefix = "rt1308-1", + }, + /* rt1308 left on link 1 */ + { + .dlc = COMP_CODEC_CONF("sdw:1:25d:1308:0"), + .name_prefix = "rt1308-1", + }, + /* two 1308s on link1 with different unique id */ + { + .dlc = COMP_CODEC_CONF("sdw:1:25d:1308:0:0"), + .name_prefix = "rt1308-1", + }, + { + .dlc = COMP_CODEC_CONF("sdw:1:25d:1308:0:2"), + .name_prefix = "rt1308-2", + }, + /* rt1308 right on link 2 */ + { + .dlc = COMP_CODEC_CONF("sdw:2:25d:1308:0"), + .name_prefix = "rt1308-2", + }, + { + .dlc = COMP_CODEC_CONF("sdw:3:25d:715:0"), + .name_prefix = "rt715", + }, + { + .dlc = COMP_CODEC_CONF("sdw:0:25d:5682:0"), + .name_prefix = "rt5682", + }, +}; + +static struct snd_soc_dai_link_component dmic_component[] = { + { + .name = "dmic-codec", + .dai_name = "dmic-hifi", + } +}; + +static struct snd_soc_dai_link_component platform_component[] = { + { + /* name might be overridden during probe */ + .name = "0000:00:1f.3" + } +}; + +/* these wrappers are only needed to avoid typecast compilation errors */ +static int sdw_startup(struct snd_pcm_substream *substream) +{ + return sdw_startup_stream(substream); +} + +static void sdw_shutdown(struct snd_pcm_substream *substream) +{ + sdw_shutdown_stream(substream); +} + +static const struct snd_soc_ops sdw_ops = { + .startup = sdw_startup, + .shutdown = sdw_shutdown, +}; + +static struct sof_sdw_codec_info codec_info_list[] = { + { + .id = 0x700, + .direction = {true, true}, + .dai_name = "rt700-aif1", + .init = sof_sdw_rt700_init, + }, + { + .id = 0x711, + .direction = {true, true}, + .dai_name = "rt711-aif1", + .init = sof_sdw_rt711_init, + }, + { + .id = 0x1308, + .acpi_id = "10EC1308", + .direction = {true, false}, + .dai_name = "rt1308-aif", + .ops = &sof_sdw_rt1308_i2s_ops, + .init = sof_sdw_rt1308_init, + }, + { + .id = 0x715, + .direction = {false, true}, + .dai_name = "rt715-aif2", + .init = sof_sdw_rt715_init, + }, + { + .id = 0x5682, + .direction = {true, true}, + .dai_name = "rt5682-sdw", + .init = sof_sdw_rt5682_init, + }, +}; + +static inline int find_codec_info_part(unsigned int part_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) + if (part_id == codec_info_list[i].id) + break; + + if (i == ARRAY_SIZE(codec_info_list)) + return -EINVAL; + + return i; +} + +static inline int find_codec_info_acpi(const u8 *acpi_id) +{ + int i; + + if (!acpi_id[0]) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) + if (!memcmp(codec_info_list[i].acpi_id, acpi_id, + ACPI_ID_LEN)) + break; + + if (i == ARRAY_SIZE(codec_info_list)) + return -EINVAL; + + return i; +} + +/* + * get BE dailink number and CPU DAI number based on sdw link adr. + * 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, + int *sdw_be_num, int *sdw_cpu_dai_num) +{ + const struct snd_soc_acpi_link_adr *link; + bool group_visited[SDW_MAX_GROUPS]; + bool no_aggregation; + int i; + + no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; + *sdw_cpu_dai_num = 0; + *sdw_be_num = 0; + + if (!links) + return -EINVAL; + + for (i = 0; i < SDW_MAX_GROUPS; i++) + group_visited[i] = false; + + for (link = links; link->num_adr; link++) { + const struct snd_soc_acpi_endpoint *endpoint; + int part_id, codec_index; + int stream; + u64 adr; + + adr = link->adr_d->adr; + part_id = SDW_PART_ID(adr); + codec_index = find_codec_info_part(part_id); + if (codec_index < 0) + return codec_index; + + endpoint = link->adr_d->endpoints; + + /* count DAI number for playback and capture */ + for_each_pcm_streams(stream) { + if (!codec_info_list[codec_index].direction[stream]) + continue; + + (*sdw_cpu_dai_num)++; + + /* count BE for each non-aggregated slave or group */ + if (!endpoint->aggregated || no_aggregation || + !group_visited[endpoint->group_id]) + (*sdw_be_num)++; + } + + if (endpoint->aggregated) + group_visited[endpoint->group_id] = true; + } + + return 0; +} + +static void init_dai_link(struct snd_soc_dai_link *dai_links, int be_id, + char *name, int playback, int capture, + struct snd_soc_dai_link_component *cpus, + int cpus_num, + struct snd_soc_dai_link_component *codecs, + int codecs_num, + int (*init)(struct snd_soc_pcm_runtime *rtd), + const struct snd_soc_ops *ops) +{ + dai_links->id = be_id; + dai_links->name = name; + dai_links->platforms = platform_component; + dai_links->num_platforms = ARRAY_SIZE(platform_component); + dai_links->nonatomic = true; + dai_links->no_pcm = 1; + dai_links->cpus = cpus; + dai_links->num_cpus = cpus_num; + dai_links->codecs = codecs; + dai_links->num_codecs = codecs_num; + dai_links->dpcm_playback = playback; + dai_links->dpcm_capture = capture; + dai_links->init = init; + dai_links->ops = ops; +} + +static bool is_unique_device(const struct snd_soc_acpi_link_adr *link, + unsigned int sdw_version, + unsigned int mfg_id, + unsigned int part_id, + unsigned int class_id, + int index_in_link + ) +{ + int i; + + for (i = 0; i < link->num_adr; i++) { + unsigned int sdw1_version, mfg1_id, part1_id, class1_id; + u64 adr; + + /* skip itself */ + if (i == index_in_link) + continue; + + adr = link->adr_d[i].adr; + + sdw1_version = SDW_VERSION(adr); + mfg1_id = SDW_MFG_ID(adr); + part1_id = SDW_PART_ID(adr); + class1_id = SDW_CLASS_ID(adr); + + if (sdw_version == sdw1_version && + mfg_id == mfg1_id && + part_id == part1_id && + class_id == class1_id) + return false; + } + + return true; +} + +static int create_codec_dai_name(struct device *dev, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link_component *codec, + int offset) +{ + int i; + + for (i = 0; i < link->num_adr; i++) { + unsigned int sdw_version, unique_id, mfg_id; + unsigned int link_id, part_id, class_id; + int codec_index, comp_index; + char *codec_str; + u64 adr; + + adr = link->adr_d[i].adr; + + sdw_version = SDW_VERSION(adr); + link_id = SDW_DISCO_LINK_ID(adr); + unique_id = SDW_UNIQUE_ID(adr); + mfg_id = SDW_MFG_ID(adr); + part_id = SDW_PART_ID(adr); + class_id = SDW_CLASS_ID(adr); + + comp_index = i + offset; + if (is_unique_device(link, sdw_version, mfg_id, part_id, + class_id, i)) { + codec_str = "sdw:%x:%x:%x:%x"; + codec[comp_index].name = + devm_kasprintf(dev, GFP_KERNEL, codec_str, + link_id, mfg_id, part_id, + class_id); + } else { + codec_str = "sdw:%x:%x:%x:%x:%x"; + codec[comp_index].name = + devm_kasprintf(dev, GFP_KERNEL, codec_str, + link_id, mfg_id, part_id, + class_id, unique_id); + } + + if (!codec[comp_index].name) + return -ENOMEM; + + codec_index = find_codec_info_part(part_id); + if (codec_index < 0) + return codec_index; + + codec[comp_index].dai_name = + codec_info_list[codec_index].dai_name; + } + + return 0; +} + +static int set_codec_init_func(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + bool playback) +{ + int i; + + for (i = 0; i < link->num_adr; i++) { + unsigned int part_id; + int codec_index; + + part_id = SDW_PART_ID(link->adr_d[i].adr); + codec_index = find_codec_info_part(part_id); + + if (codec_index < 0) + return codec_index; + + if (codec_info_list[codec_index].init) + codec_info_list[codec_index].init(link, dai_links, + &codec_info_list[codec_index], + playback); + } + + return 0; +} + +/* + * check endpoint status in slaves and gather link ID for all slaves in + * the same group to generate different CPU DAI. Now only support + * one sdw link with all slaves set with only single group id. + * + * one slave on one sdw link with aggregated = 0 + * one sdw BE DAI <---> one-cpu DAI <---> one-codec DAI + * + * two or more slaves on one sdw link with aggregated = 0 + * one sdw BE DAI <---> one-cpu DAI <---> multi-codec DAIs + * + * multiple links with multiple slaves with aggregated = 1 + * one sdw BE DAI <---> 1 .. N CPU DAIs <----> 1 .. N codec DAIs + */ +static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, + struct device *dev, int *cpu_dai_id, int *cpu_dai_num, + int *codec_num, int *group_id, + bool *group_generated) +{ + const struct snd_soc_acpi_adr_device *adr_d; + const struct snd_soc_acpi_link_adr *adr_next; + bool no_aggregation; + int index = 0; + + no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; + *codec_num = adr_link->num_adr; + adr_d = adr_link->adr_d; + + /* make sure the link mask has a single bit set */ + if (!is_power_of_2(adr_link->mask)) + return -EINVAL; + + cpu_dai_id[index++] = ffs(adr_link->mask) - 1; + if (!adr_d->endpoints->aggregated || no_aggregation) { + *cpu_dai_num = 1; + *group_id = 0; + return 0; + } + + *group_id = adr_d->endpoints->group_id; + + /* gather other link ID of slaves in the same group */ + for (adr_next = adr_link + 1; adr_next && adr_next->num_adr; + adr_next++) { + const struct snd_soc_acpi_endpoint *endpoint; + + endpoint = adr_next->adr_d->endpoints; + if (!endpoint->aggregated || + endpoint->group_id != *group_id) + continue; + + /* make sure the link mask has a single bit set */ + if (!is_power_of_2(adr_next->mask)) + return -EINVAL; + + if (index >= SDW_MAX_CPU_DAIS) { + dev_err(dev, " cpu_dai_id array overflows"); + return -EINVAL; + } + + cpu_dai_id[index++] = ffs(adr_next->mask) - 1; + *codec_num += adr_next->num_adr; + } + + /* + * indicate CPU DAIs for this group have been generated + * to avoid generating CPU DAIs for this group again. + */ + group_generated[*group_id] = true; + *cpu_dai_num = index; + + return 0; +} + +static int create_sdw_dailink(struct device *dev, int *be_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) +{ + const struct snd_soc_acpi_link_adr *link_next; + struct snd_soc_dai_link_component *codecs; + int cpu_dai_id[SDW_MAX_CPU_DAIS]; + int cpu_dai_num, cpu_dai_index; + unsigned int part_id, group_id; + int codec_idx = 0; + int i = 0, j = 0; + int codec_index; + int codec_num; + int stream; + int ret; + int k; + + ret = get_slave_info(link, dev, cpu_dai_id, &cpu_dai_num, &codec_num, + &group_id, group_generated); + if (ret) + return ret; + + codecs = devm_kcalloc(dev, codec_num, sizeof(*codecs), GFP_KERNEL); + if (!codecs) + return -ENOMEM; + + /* generate codec name on different links in the same group */ + for (link_next = link; link_next && link_next->num_adr && + i < cpu_dai_num; link_next++) { + const struct snd_soc_acpi_endpoint *endpoints; + + endpoints = link_next->adr_d->endpoints; + if (group_id && (!endpoints->aggregated || + endpoints->group_id != group_id)) + continue; + + /* skip the link excluded by this processed group */ + if (cpu_dai_id[i] != ffs(link_next->mask) - 1) + continue; + + ret = create_codec_dai_name(dev, link_next, codecs, codec_idx); + if (ret < 0) + return ret; + + /* check next link to create codec dai in the processed group */ + i++; + codec_idx += link_next->num_adr; + } + + /* find codec info to create BE DAI */ + part_id = SDW_PART_ID(link->adr_d[0].adr); + codec_index = find_codec_info_part(part_id); + if (codec_index < 0) + return codec_index; + + cpu_dai_index = *cpu_id; + for_each_pcm_streams(stream) { + char *name, *cpu_name; + int playback, capture; + static const char * const sdw_stream_name[] = { + "SDW%d-Playback", + "SDW%d-Capture", + }; + + if (!codec_info_list[codec_index].direction[stream]) + continue; + + /* create stream name according to first link id */ + name = devm_kasprintf(dev, GFP_KERNEL, + sdw_stream_name[stream], cpu_dai_id[0]); + if (!name) + return -ENOMEM; + + /* + * generate CPU DAI name base on the sdw link ID and + * PIN ID with offset of 2 according to sdw dai driver. + */ + for (k = 0; k < cpu_dai_num; k++) { + cpu_name = devm_kasprintf(dev, GFP_KERNEL, + "SDW%d Pin%d", cpu_dai_id[k], + j + SDW_INTEL_BIDIR_PDI_BASE); + if (!cpu_name) + return -ENOMEM; + + if (cpu_dai_index >= sdw_cpu_dai_num) { + dev_err(dev, "invalid cpu dai index %d", + cpu_dai_index); + return -EINVAL; + } + + cpus[cpu_dai_index++].dai_name = cpu_name; + } + + if (*be_index >= sdw_be_num) { + dev_err(dev, " invalid be dai index %d", *be_index); + return -EINVAL; + } + + if (*cpu_id >= sdw_cpu_dai_num) { + dev_err(dev, " invalid cpu dai index %d", *cpu_id); + return -EINVAL; + } + + playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); + capture = (stream == SNDRV_PCM_STREAM_CAPTURE); + init_dai_link(dai_links + *be_index, *be_index, name, + playback, capture, + cpus + *cpu_id, cpu_dai_num, + codecs, codec_num, + NULL, &sdw_ops); + + ret = set_codec_init_func(link, dai_links + (*be_index)++, + playback); + if (ret < 0) { + dev_err(dev, "failed to init codec %d", codec_index); + return ret; + } + + *cpu_id += cpu_dai_num; + j++; + } + + 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; +} + +static int sof_card_dai_links_create(struct device *dev, + struct snd_soc_acpi_mach *mach, + struct snd_soc_card *card) +{ + int ssp_num, sdw_be_num = 0, hdmi_num = 0, dmic_num; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) + struct snd_soc_dai_link_component *idisp_components; +#endif + struct snd_soc_dai_link_component *ssp_components; + struct snd_soc_acpi_mach_params *mach_params; + const struct snd_soc_acpi_link_adr *adr_link; + struct snd_soc_dai_link_component *cpus; + bool group_generated[SDW_MAX_GROUPS]; + int ssp_codec_index, ssp_mask; + struct snd_soc_dai_link *links; + int num_links, link_id = 0; + char *name, *cpu_name; + int total_cpu_dai_num; + int sdw_cpu_dai_num; + int i, j, be_id = 0; + int cpu_id = 0; + int comp_num; + int ret; + + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ + for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) + codec_info_list[i].amp_num = 0; + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) + hdmi_num = sof_sdw_quirk & SOF_SDW_TGL_HDMI ? + SOF_TGL_HDMI_COUNT : SOF_PRE_TGL_HDMI_COUNT; +#endif + + ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk); + /* + * on generic tgl platform, I2S or sdw mode is supported + * based on board rework. A ACPI device is registered in + * system only when I2S mode is supported, not sdw mode. + * Here check ACPI ID to confirm I2S is supported. + */ + ssp_codec_index = find_codec_info_acpi(mach->id); + ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0; + comp_num = hdmi_num + ssp_num; + + mach_params = &mach->mach_params; + ret = get_sdw_dailink_info(mach_params->links, + &sdw_be_num, &sdw_cpu_dai_num); + if (ret < 0) { + dev_err(dev, "failed to get sdw link info %d", ret); + return ret; + } + + /* enable dmic01 & dmic16k */ + dmic_num = (sof_sdw_quirk & SOF_SDW_PCH_DMIC) ? 2 : 0; + comp_num += dmic_num; + + dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d", sdw_be_num, ssp_num, + dmic_num, hdmi_num); + + /* allocate BE dailinks */ + num_links = comp_num + sdw_be_num; + links = devm_kcalloc(dev, num_links, sizeof(*links), GFP_KERNEL); + + /* allocated CPU DAIs */ + total_cpu_dai_num = comp_num + sdw_cpu_dai_num; + cpus = devm_kcalloc(dev, total_cpu_dai_num, sizeof(*cpus), + GFP_KERNEL); + + if (!links || !cpus) + return -ENOMEM; + + /* SDW */ + if (!sdw_be_num) + goto SSP; + + adr_link = mach_params->links; + if (!adr_link) + return -EINVAL; + + /* + * SoundWire Slaves aggregated in the same group may be + * located on different hardware links. Clear array to indicate + * CPU DAIs for this group have not been generated. + */ + for (i = 0; i < SDW_MAX_GROUPS; i++) + group_generated[i] = false; + + /* generate DAI links by each sdw link */ + for (; adr_link->num_adr; adr_link++) { + const struct snd_soc_acpi_endpoint *endpoint; + + endpoint = adr_link->adr_d->endpoints; + if (endpoint->aggregated && !endpoint->group_id) { + dev_err(dev, "invalid group id on link %x", + adr_link->mask); + continue; + } + + /* this group has been generated */ + if (endpoint->aggregated && + group_generated[endpoint->group_id]) + continue; + + ret = create_sdw_dailink(dev, &be_id, links, sdw_be_num, + sdw_cpu_dai_num, cpus, adr_link, + &cpu_id, group_generated); + if (ret < 0) { + dev_err(dev, "failed to create dai link %d", be_id); + return -ENOMEM; + } + } + + /* 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) + goto DMIC; + + for (i = 0, j = 0; ssp_mask; i++, ssp_mask >>= 1) { + struct sof_sdw_codec_info *info; + int playback, capture; + char *codec_name; + + if (!(ssp_mask & 0x1)) + continue; + + name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d-Codec", i); + if (!name) + return -ENOMEM; + + cpu_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", i); + if (!cpu_name) + return -ENOMEM; + + ssp_components = devm_kzalloc(dev, sizeof(*ssp_components), + GFP_KERNEL); + if (!ssp_components) + return -ENOMEM; + + info = &codec_info_list[ssp_codec_index]; + codec_name = devm_kasprintf(dev, GFP_KERNEL, "i2c-%s:0%d", + info->acpi_id, j++); + if (!codec_name) + return -ENOMEM; + + ssp_components->name = codec_name; + ssp_components->dai_name = info->dai_name; + cpus[cpu_id].dai_name = cpu_name; + + playback = info->direction[SNDRV_PCM_STREAM_PLAYBACK]; + capture = info->direction[SNDRV_PCM_STREAM_CAPTURE]; + init_dai_link(links + link_id, be_id, name, + playback, capture, + cpus + cpu_id, 1, + ssp_components, 1, + NULL, info->ops); + + ret = info->init(NULL, links + link_id, info, 0); + if (ret < 0) + return ret; + + INC_ID(be_id, cpu_id, link_id); + } + +DMIC: + /* dmic */ + if (dmic_num > 0) { + cpus[cpu_id].dai_name = "DMIC01 Pin"; + init_dai_link(links + link_id, 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); + + cpus[cpu_id].dai_name = "DMIC16k Pin"; + init_dai_link(links + link_id, 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); + } + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) + /* HDMI */ + if (hdmi_num > 0) { + idisp_components = devm_kcalloc(dev, hdmi_num, + sizeof(*idisp_components), + GFP_KERNEL); + if (!idisp_components) + return -ENOMEM; + } + + for (i = 0; i < hdmi_num; i++) { + name = devm_kasprintf(dev, GFP_KERNEL, + "iDisp%d", i + 1); + if (!name) + return -ENOMEM; + + idisp_components[i].name = "ehdaudio0D2"; + idisp_components[i].dai_name = devm_kasprintf(dev, + GFP_KERNEL, + "intel-hdmi-hifi%d", + i + 1); + if (!idisp_components[i].dai_name) + return -ENOMEM; + + cpu_name = devm_kasprintf(dev, GFP_KERNEL, + "iDisp%d Pin", i + 1); + if (!cpu_name) + return -ENOMEM; + + cpus[cpu_id].dai_name = cpu_name; + init_dai_link(links + link_id, 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); + } +#endif + + card->dai_link = links; + card->num_links = num_links; + + return 0; +} + +/* SoC card */ +static const char sdw_card_long_name[] = "Intel Soundwire SOF"; + +static struct snd_soc_card card_sof_sdw = { + .name = "soundwire", + .late_probe = sof_sdw_hdmi_card_late_probe, + .codec_conf = codec_conf, + .num_configs = ARRAY_SIZE(codec_conf), +}; + +static int mc_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &card_sof_sdw; + struct snd_soc_acpi_mach *mach; + struct mc_private *ctx; + int ret; + + dev_dbg(&pdev->dev, "Entry %s\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + dmi_check_system(sof_sdw_quirk_table); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + card->dev = &pdev->dev; + + mach = pdev->dev.platform_data; + ret = sof_card_dai_links_create(&pdev->dev, mach, + card); + if (ret < 0) + return ret; + + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + snd_soc_card_set_drvdata(card, ctx); + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "cfg-spk:%d", + (sof_sdw_quirk & SOF_SDW_FOUR_SPK) ? 4 : 2); + if (!card->components) + return -ENOMEM; + + card->long_name = sdw_card_long_name; + + /* Register the card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, card); + + return ret; +} + +static struct platform_driver sof_sdw_driver = { + .driver = { + .name = "sof_sdw", + .pm = &snd_soc_pm_ops, + }, + .probe = mc_probe, +}; + +module_platform_driver(sof_sdw_driver); + +MODULE_DESCRIPTION("ASoC SoundWire Generic Machine driver"); +MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>"); +MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>"); +MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sof_sdw"); diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h new file mode 100644 index 000000000000..dd593ff3575b --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Copyright (c) 2020 Intel Corporation + */ + +/* + * sof_sdw_common.h - prototypes for common helpers + */ + +#ifndef SND_SOC_SOF_SDW_COMMON_H +#define SND_SOC_SOF_SDW_COMMON_H + +#include <linux/bits.h> +#include <linux/types.h> + +#define MAX_NO_PROPS 2 +#define MAX_HDMI_NUM 4 +#define SDW_DMIC_DAI_ID 4 +#define SDW_MAX_CPU_DAIS 16 +#define SDW_INTEL_BIDIR_PDI_BASE 2 + +/* 8 combinations with 4 links + unused group 0 */ +#define SDW_MAX_GROUPS 9 + +enum { + SOF_RT711_JD_SRC_JD1 = 1, + SOF_RT711_JD_SRC_JD2 = 2, +}; + +enum { + SOF_PRE_TGL_HDMI_COUNT = 3, + SOF_TGL_HDMI_COUNT = 4, +}; + +enum { + SOF_I2S_SSP0 = BIT(0), + SOF_I2S_SSP1 = BIT(1), + SOF_I2S_SSP2 = BIT(2), + SOF_I2S_SSP3 = BIT(3), + SOF_I2S_SSP4 = BIT(4), + SOF_I2S_SSP5 = BIT(5), +}; + +#define SOF_RT711_JDSRC(quirk) ((quirk) & GENMASK(1, 0)) +#define SOF_SDW_FOUR_SPK BIT(2) +#define SOF_SDW_TGL_HDMI BIT(3) +#define SOF_SDW_PCH_DMIC BIT(4) +#define SOF_SSP_PORT(x) (((x) & GENMASK(5, 0)) << 5) +#define SOF_SSP_GET_PORT(quirk) (((quirk) >> 5) & GENMASK(5, 0)) +#define SOF_RT715_DAI_ID_FIX BIT(11) +#define SOF_SDW_NO_AGGREGATION BIT(12) + +struct sof_sdw_codec_info { + const int id; + int amp_num; + const u8 acpi_id[ACPI_ID_LEN]; + const bool direction[2]; // playback & capture support + const char *dai_name; + const struct snd_soc_ops *ops; + + int (*init)(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); +}; + +struct mc_private { + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; + struct snd_soc_jack sdw_headset; +}; + +extern unsigned long sof_sdw_quirk; + +/* generic HDMI support */ +int sof_sdw_hdmi_init(struct snd_soc_pcm_runtime *rtd); + +int sof_sdw_hdmi_card_late_probe(struct snd_soc_card *card); + +/* DMIC support */ +int sof_sdw_dmic_init(struct snd_soc_pcm_runtime *rtd); + +/* RT711 support */ +int sof_sdw_rt711_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); + +/* RT700 support */ +int sof_sdw_rt700_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); + +/* RT1308 support */ +extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops; + +int sof_sdw_rt1308_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); + +/* RT715 support */ +int sof_sdw_rt715_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); + +/* RT5682 support */ +int sof_sdw_rt5682_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); + +#endif diff --git a/sound/soc/intel/boards/sof_sdw_dmic.c b/sound/soc/intel/boards/sof_sdw_dmic.c new file mode 100644 index 000000000000..e92176bf0ad4 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_dmic.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_dmic - Helpers to handle dmic from generic machine driver + */ + +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "sof_sdw_common.h" + +static const struct snd_soc_dapm_widget dmic_widgets[] = { + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route dmic_map[] = { + /* digital mics */ + {"DMic", NULL, "SoC DMIC"}, +}; + +int sof_sdw_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; +} + diff --git a/sound/soc/intel/boards/sof_sdw_hdmi.c b/sound/soc/intel/boards/sof_sdw_hdmi.c new file mode 100644 index 000000000000..c7b5612a39e6 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_hdmi.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_hdmi - Helpers to handle HDMI from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <sound/jack.h> +#include "sof_sdw_common.h" +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +static struct snd_soc_jack hdmi[MAX_HDMI_NUM]; + +struct hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +int sof_sdw_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct 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; +} + +#define NAME_SIZE 32 +int sof_sdw_hdmi_card_late_probe(struct snd_soc_card *card) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &hdmi[i], + NULL, 0); + + if (err) + return err; + + err = snd_jack_add_new_kctl(hdmi[i].jack, + jack_name, SND_JACK_AVOUT); + if (err) + dev_warn(component->dev, "failed creating Jack kctl\n"); + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} +#else +int hdmi_card_late_probe(struct snd_soc_card *card) +{ + return 0; +} +#endif diff --git a/sound/soc/intel/boards/sof_sdw_rt1308.c b/sound/soc/intel/boards/sof_sdw_rt1308.c new file mode 100644 index 000000000000..321768e54d08 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt1308.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_rt1308 - Helpers to handle RT1308 from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "sof_sdw_common.h" +#include "../../codecs/rt1308.h" + +static const struct snd_soc_dapm_widget rt1308_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +/* + * dapm routes for rt1308 will be registered dynamically according + * to the number of rt1308 used. The first two entries will be registered + * for one codec case, and the last two entries are also registered + * if two 1308s are used. + */ +static const struct snd_soc_dapm_route rt1308_map[] = { + { "Speaker", NULL, "rt1308-1 SPOL" }, + { "Speaker", NULL, "rt1308-1 SPOR" }, + { "Speaker", NULL, "rt1308-2 SPOL" }, + { "Speaker", NULL, "rt1308-2 SPOR" }, +}; + +static const struct snd_kcontrol_new rt1308_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static int first_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s spk:rt1308", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_add_card_controls(card, rt1308_controls, + ARRAY_SIZE(rt1308_controls)); + if (ret) { + dev_err(card->dev, "rt1308 controls addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, rt1308_widgets, + ARRAY_SIZE(rt1308_widgets)); + if (ret) { + dev_err(card->dev, "rt1308 widgets addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map, 2); + if (ret) + dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); + + return ret; +} + +static int second_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map + 2, 2); + if (ret) + dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret); + + return ret; +} + +static int all_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + + ret = first_spk_init(rtd); + if (ret) + return ret; + + return second_spk_init(rtd); +} + +static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int clk_id, clk_freq, pll_out; + int err; + + clk_id = RT1308_PLL_S_MCLK; + clk_freq = 38400000; + + pll_out = params_rate(params) * 512; + + /* Set rt1308 pll */ + err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); + if (err < 0) { + dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err); + return err; + } + + /* Set rt1308 sysclk */ + err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err); + return err; + } + + return 0; +} + +/* machine stream operations */ +struct snd_soc_ops sof_sdw_rt1308_i2s_ops = { + .hw_params = rt1308_i2s_hw_params, +}; + +int sof_sdw_rt1308_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + info->amp_num++; + if (info->amp_num == 1) + dai_links->init = first_spk_init; + + if (info->amp_num == 2) { + /* + * if two 1308s are in one dai link, the init function + * in this dai link will be first set for the first speaker, + * and it should be reset to initialize all speakers when + * the second speaker is found. + */ + if (dai_links->init) + dai_links->init = all_spk_init; + else + dai_links->init = second_spk_init; + } + + return 0; +} diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c new file mode 100644 index 000000000000..5aa6211a1ed9 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt5682.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_rt5682 - Helpers to handle RT5682 from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <sound/jack.h> +#include "sof_sdw_common.h" + +static const struct snd_soc_dapm_widget rt5682_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route rt5682_map[] = { + /*Headphones*/ + { "Headphone", NULL, "rt5682 HPOL" }, + { "Headphone", NULL, "rt5682 HPOR" }, + { "rt5682 IN1P", NULL, "Headset Mic" }, +}; + +static const struct snd_kcontrol_new rt5682_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static struct snd_soc_jack_pin rt5682_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s hs:rt5682", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_add_card_controls(card, rt5682_controls, + ARRAY_SIZE(rt5682_controls)); + if (ret) { + dev_err(card->dev, "rt5682 control addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, rt5682_widgets, + ARRAY_SIZE(rt5682_widgets)); + if (ret) { + dev_err(card->dev, "rt5682 widgets addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, rt5682_map, + ARRAY_SIZE(rt5682_map)); + + if (ret) { + dev_err(card->dev, "rt5682 map addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + rt5682_jack_pins, + ARRAY_SIZE(rt5682_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_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->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +int sof_sdw_rt5682_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + /* + * headset should be initialized once. + * Do it with dai link for playback. + */ + if (!playback) + return 0; + + dai_links->init = rt5682_rtd_init; + + return 0; +} diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c new file mode 100644 index 000000000000..2ee4e6910d7f --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_rt700 - Helpers to handle RT700 from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <sound/jack.h> +#include "sof_sdw_common.h" + +static const struct snd_soc_dapm_widget rt700_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route rt700_map[] = { + /* Headphones */ + { "Headphones", NULL, "HP" }, + { "Speaker", NULL, "SPK" }, + { "MIC2", NULL, "AMIC" }, +}; + +static const struct snd_kcontrol_new rt700_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static struct snd_soc_jack_pin rt700_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "AMIC", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s hs:rt700", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_add_card_controls(card, rt700_controls, + ARRAY_SIZE(rt700_controls)); + if (ret) { + dev_err(card->dev, "rt700 controls addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, rt700_widgets, + ARRAY_SIZE(rt700_widgets)); + if (ret) { + dev_err(card->dev, "rt700 widgets addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, rt700_map, + ARRAY_SIZE(rt700_map)); + + if (ret) { + dev_err(card->dev, "rt700 map addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + rt700_jack_pins, + ARRAY_SIZE(rt700_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ret = snd_soc_component_set_jack(component, jack, NULL); + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +int sof_sdw_rt700_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + /* + * headset should be initialized once. + * Do it with dai link for playback. + */ + if (!playback) + return 0; + + dai_links->init = rt700_rtd_init; + + return 0; +} diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c new file mode 100644 index 000000000000..2a4917e3d561 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt711.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_rt711 - Helpers to handle RT711 from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <sound/jack.h> +#include "sof_sdw_common.h" + +/* + * Note this MUST be called before snd_soc_register_card(), so that the props + * are in place before the codec component driver's probe function parses them. + */ +static int rt711_add_codec_device_props(const char *sdw_dev_name) +{ + struct property_entry props[MAX_NO_PROPS] = {}; + struct device *sdw_dev; + int ret; + + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_dev_name); + if (!sdw_dev) + return -EPROBE_DEFER; + + if (SOF_RT711_JDSRC(sof_sdw_quirk)) { + props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", + SOF_RT711_JDSRC(sof_sdw_quirk)); + } + + ret = device_add_properties(sdw_dev, props); + put_device(sdw_dev); + + return ret; +} + +static const struct snd_soc_dapm_widget rt711_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route rt711_map[] = { + /* Headphones */ + { "Headphone", NULL, "rt711 HP" }, + { "rt711 MIC2", NULL, "Headset Mic" }, +}; + +static const struct snd_kcontrol_new rt711_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static struct snd_soc_jack_pin rt711_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s hs:rt711", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_add_card_controls(card, rt711_controls, + ARRAY_SIZE(rt711_controls)); + if (ret) { + dev_err(card->dev, "rt711 controls addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, rt711_widgets, + ARRAY_SIZE(rt711_widgets)); + if (ret) { + dev_err(card->dev, "rt711 widgets addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, rt711_map, + ARRAY_SIZE(rt711_map)); + + if (ret) { + dev_err(card->dev, "rt711 map addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + rt711_jack_pins, + ARRAY_SIZE(rt711_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +int sof_sdw_rt711_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + int ret; + + /* + * headset should be initialized once. + * Do it with dai link for playback. + */ + if (!playback) + return 0; + + ret = rt711_add_codec_device_props("sdw:0:25d:711:0"); + if (ret < 0) + return ret; + + dai_links->init = rt711_rtd_init; + + return 0; +} diff --git a/sound/soc/intel/boards/sof_sdw_rt715.c b/sound/soc/intel/boards/sof_sdw_rt715.c new file mode 100644 index 000000000000..321e1cbc03ed --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt715.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_rt715 - Helpers to handle RT715 from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "sof_sdw_common.h" + +static int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s mic:rt715", + card->components); + if (!card->components) + return -ENOMEM; + + return 0; +} + +int sof_sdw_rt715_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + 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/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 4a5adae1d785..f5092bc48364 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -65,7 +65,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { }, { .id = "104C5122", - .drv_name = "bxt-pcm512x", + .drv_name = "sof_pcm512x", .sof_fw_filename = "sof-apl.ri", .sof_tplg_filename = "sof-apl-pcm512x.tplg", }, diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index d0fb43c2b9f6..2752dc955733 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -174,6 +174,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-cx2072x.tplg", }, + { + .id = "104C5122", + .drv_name = "sof_pcm512x", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-src-50khz-pcm512x.tplg", + }, + #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) /* * This is always last in the table so that it is selected only when diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index f55634c4c2e8..bcedec6c6117 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -59,42 +59,112 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines); -static const u64 rt711_0_adr[] = { - 0x000010025D071100 +static const struct snd_soc_acpi_endpoint single_endpoint = { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, }; -static const u64 rt1308_1_adr[] = { - 0x000110025D130800 +static const struct snd_soc_acpi_endpoint spk_l_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1, }; -static const u64 rt1308_2_adr[] = { - 0x000210025D130800 +static const struct snd_soc_acpi_endpoint spk_r_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, }; -static const u64 rt715_3_adr[] = { - 0x000310025D071500 +static const struct snd_soc_acpi_adr_device rt700_1_adr[] = { + { + .adr = 0x000110025D070000, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_link_adr cml_rvp[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt700_1_adr), + .adr_d = rt700_1_adr, + }, + {} +}; + +static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { + { + .adr = 0x000010025D071100, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { + { + .adr = 0x000110025D130800, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1308_2_adr[] = { + { + .adr = 0x000210025D130800, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { + { + .adr = 0x000110025D130800, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { + { + .adr = 0x000210025D130800, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { + { + .adr = 0x000310025D071500, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } }; static const struct snd_soc_acpi_link_adr cml_3_in_1_default[] = { { .mask = BIT(0), .num_adr = ARRAY_SIZE(rt711_0_adr), - .adr = rt711_0_adr, + .adr_d = rt711_0_adr, }, { .mask = BIT(1), - .num_adr = ARRAY_SIZE(rt1308_1_adr), - .adr = rt1308_1_adr, + .num_adr = ARRAY_SIZE(rt1308_1_group1_adr), + .adr_d = rt1308_1_group1_adr, }, { .mask = BIT(2), - .num_adr = ARRAY_SIZE(rt1308_2_adr), - .adr = rt1308_2_adr, + .num_adr = ARRAY_SIZE(rt1308_2_group1_adr), + .adr_d = rt1308_2_group1_adr, }, { .mask = BIT(3), .num_adr = ARRAY_SIZE(rt715_3_adr), - .adr = rt715_3_adr, + .adr_d = rt715_3_adr, }, {} }; @@ -103,17 +173,17 @@ static const struct snd_soc_acpi_link_adr cml_3_in_1_mono_amp[] = { { .mask = BIT(0), .num_adr = ARRAY_SIZE(rt711_0_adr), - .adr = rt711_0_adr, + .adr_d = rt711_0_adr, }, { .mask = BIT(1), .num_adr = ARRAY_SIZE(rt1308_1_adr), - .adr = rt1308_1_adr, + .adr_d = rt1308_1_adr, }, { .mask = BIT(3), .num_adr = ARRAY_SIZE(rt715_3_adr), - .adr = rt715_3_adr, + .adr_d = rt715_3_adr, }, {} }; @@ -122,7 +192,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = { { .link_mask = 0xF, /* 4 active links required */ .links = cml_3_in_1_default, - .drv_name = "sdw_rt711_rt1308_rt715", + .drv_name = "sof_sdw", .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt711-rt1308-rt715.tplg", }, @@ -134,13 +204,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = { */ .link_mask = 0xF, .links = cml_3_in_1_mono_amp, - .drv_name = "sdw_rt711_rt1308_rt715", + .drv_name = "sof_sdw", .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt711-rt1308-mono-rt715.tplg", }, { .link_mask = 0x2, /* RT700 connected on Link1 */ - .drv_name = "sdw_rt700", + .links = cml_rvp, + .drv_name = "sof_sdw", .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt700.tplg", }, diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c index 752733013d54..ef8500349f2f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-icl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -33,55 +33,112 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); -static const u64 rt700_0_adr[] = { - 0x000010025D070000 +static const struct snd_soc_acpi_endpoint single_endpoint = { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, +}; + +static const struct snd_soc_acpi_endpoint spk_l_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_r_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, +}; + +static const struct snd_soc_acpi_adr_device rt700_0_adr[] = { + { + .adr = 0x000010025D070000, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } }; static const struct snd_soc_acpi_link_adr icl_rvp[] = { { .mask = BIT(0), .num_adr = ARRAY_SIZE(rt700_0_adr), - .adr = rt700_0_adr, + .adr_d = rt700_0_adr, }, {} }; -static const u64 rt711_0_adr[] = { - 0x000010025D071100 +static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { + { + .adr = 0x000010025D071100, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { + { + .adr = 0x000110025D130800, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } }; -static const u64 rt1308_1_adr[] = { - 0x000110025D130800 +static const struct snd_soc_acpi_adr_device rt1308_2_adr[] = { + { + .adr = 0x000210025D130800, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } }; -static const u64 rt1308_2_adr[] = { - 0x000210025D130800 +static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { + { + .adr = 0x000110025D130800, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + } }; -static const u64 rt715_3_adr[] = { - 0x000310025D071500 +static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { + { + .adr = 0x000210025D130800, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { + { + .adr = 0x000310025D071500, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } }; static const struct snd_soc_acpi_link_adr icl_3_in_1_default[] = { { .mask = BIT(0), .num_adr = ARRAY_SIZE(rt711_0_adr), - .adr = rt711_0_adr, + .adr_d = rt711_0_adr, }, { .mask = BIT(1), - .num_adr = ARRAY_SIZE(rt1308_1_adr), - .adr = rt1308_1_adr, + .num_adr = ARRAY_SIZE(rt1308_1_group1_adr), + .adr_d = rt1308_1_group1_adr, }, { .mask = BIT(2), - .num_adr = ARRAY_SIZE(rt1308_2_adr), - .adr = rt1308_2_adr, + .num_adr = ARRAY_SIZE(rt1308_2_group1_adr), + .adr_d = rt1308_2_group1_adr, }, { .mask = BIT(3), .num_adr = ARRAY_SIZE(rt715_3_adr), - .adr = rt715_3_adr, + .adr_d = rt715_3_adr, }, {} }; @@ -90,17 +147,17 @@ static const struct snd_soc_acpi_link_adr icl_3_in_1_mono_amp[] = { { .mask = BIT(0), .num_adr = ARRAY_SIZE(rt711_0_adr), - .adr = rt711_0_adr, + .adr_d = rt711_0_adr, }, { .mask = BIT(1), .num_adr = ARRAY_SIZE(rt1308_1_adr), - .adr = rt1308_1_adr, + .adr_d = rt1308_1_adr, }, { .mask = BIT(3), .num_adr = ARRAY_SIZE(rt715_3_adr), - .adr = rt715_3_adr, + .adr_d = rt715_3_adr, }, {} }; @@ -109,21 +166,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[] = { { .link_mask = 0xF, /* 4 active links required */ .links = icl_3_in_1_default, - .drv_name = "sdw_rt711_rt1308_rt715", + .drv_name = "sof_sdw", .sof_fw_filename = "sof-icl.ri", .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715.tplg", }, { .link_mask = 0xB, /* 3 active links required */ .links = icl_3_in_1_mono_amp, - .drv_name = "sdw_rt711_rt1308_rt715", + .drv_name = "sof_sdw", .sof_fw_filename = "sof-icl.ri", .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715-mono.tplg", }, { .link_mask = 0x1, /* rt700 connected on link0 */ .links = icl_rvp, - .drv_name = "sdw_rt700", + .drv_name = "sof_sdw", .sof_fw_filename = "sof-icl.ri", .sof_tplg_filename = "sof-icl-rt700.tplg", }, diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index ed2b125f6a11..4388a32718d8 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -2,20 +2,50 @@ /* * soc-apci-intel-jsl-match.c - tables and support for JSL ACPI enumeration. * - * Copyright (c) 2019, Intel Corporation. + * Copyright (c) 2019-2020, Intel Corporation. * */ #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> +static struct snd_soc_acpi_codecs jsl_7219_98373_codecs = { + .num_codecs = 1, + .codecs = {"MX98373"} +}; + +static struct snd_soc_acpi_codecs rt1015_spk = { + .num_codecs = 1, + .codecs = {"10EC1015"} +}; + +/* + * When adding new entry to the snd_soc_acpi_intel_jsl_machines array, + * use .quirk_data member to distinguish different machine driver, + * and keep ACPI .id field unchanged for the common codec. + */ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { { .id = "DLGS7219", .drv_name = "sof_da7219_max98373", - .machine_quirk = snd_soc_acpi_codec_list, .sof_fw_filename = "sof-jsl.ri", .sof_tplg_filename = "sof-jsl-da7219.tplg", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &jsl_7219_98373_codecs, + }, + { + .id = "DLGS7219", + .drv_name = "sof_da7219_max98360a", + .sof_fw_filename = "sof-jsl.ri", + .sof_tplg_filename = "sof-jsl-da7219-mx98360a.tplg", + }, + { + .id = "10EC5682", + .drv_name = "jsl_rt5682_rt1015", + .sof_fw_filename = "sof-jsl.ri", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rt1015_spk, + .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index 5984dd151f3e..449d9d2286ae 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -14,20 +14,61 @@ static struct snd_soc_acpi_codecs tgl_codecs = { .codecs = {"MX98357A"} }; -static const u64 rt711_0_adr[] = { - 0x000010025D071100 +static const struct snd_soc_acpi_endpoint single_endpoint = { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, }; -static const u64 rt1308_1_adr[] = { - 0x000120025D130800, - 0x000122025D130800 +static const struct snd_soc_acpi_endpoint spk_l_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_r_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, +}; + +static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { + { + .adr = 0x000010025D071100, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { + { + .adr = 0x000120025D130800, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + }, + { + .adr = 0x000122025D130800, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt5682_0_adr[] = { + { + .adr = 0x000021025D568200, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } }; static const struct snd_soc_acpi_link_adr tgl_i2s_rt1308[] = { { .mask = BIT(0), .num_adr = ARRAY_SIZE(rt711_0_adr), - .adr = rt711_0_adr, + .adr_d = rt711_0_adr, }, {} }; @@ -36,24 +77,38 @@ static const struct snd_soc_acpi_link_adr tgl_rvp[] = { { .mask = BIT(0), .num_adr = ARRAY_SIZE(rt711_0_adr), - .adr = rt711_0_adr, + .adr_d = rt711_0_adr, }, { .mask = BIT(1), .num_adr = ARRAY_SIZE(rt1308_1_adr), - .adr = rt1308_1_adr, + .adr_d = rt1308_1_adr, }, {} }; +static const struct snd_soc_acpi_link_adr tgl_chromebook_base[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt5682_0_adr), + .adr_d = rt5682_0_adr, + }, + {} +}; + +static struct snd_soc_acpi_codecs tgl_max98373_amp = { + .num_codecs = 1, + .codecs = {"MX98373"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { { .id = "10EC1308", - .drv_name = "rt711_rt1308", + .drv_name = "sof_sdw", .link_mask = 0x1, /* RT711 on SoundWire link0 */ .links = tgl_i2s_rt1308, .sof_fw_filename = "sof-tgl.ri", - .sof_tplg_filename = "sof-tgl-rt711-rt1308.tplg", + .sof_tplg_filename = "sof-tgl-rt711-i2s-rt1308.tplg", }, { .id = "10EC5682", @@ -63,6 +118,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-max98357a-rt5682.tplg", }, + { + .id = "10EC5682", + .drv_name = "tgl_max98373_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &tgl_max98373_amp, + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-max98373-rt5682.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines); @@ -72,10 +135,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { { .link_mask = 0x3, /* rt711 on link 0 and 2 rt1308s on link 1 */ .links = tgl_rvp, - .drv_name = "sdw_rt711_rt1308_rt715", + .drv_name = "sof_sdw", .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-rt711-rt1308.tplg", }, + { + .link_mask = 0x1, /* this will only enable rt5682 for now */ + .links = tgl_chromebook_base, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-rt5682.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_sdw_machines); diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index 033d7c05d7fb..c183f8e94ee4 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -476,7 +476,7 @@ static int hsw_pcm_hw_params(struct snd_soc_component *component, u8 channels; int ret, dai; - dai = mod_map[rtd->cpu_dai->id].dai_id; + dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; pcm_data = &pdata->pcm[dai][substream->stream]; /* check if we are being called a subsequent time */ @@ -494,7 +494,7 @@ static int hsw_pcm_hw_params(struct snd_soc_component *component, } pcm_data->allocated = false; - pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id, + pcm_data->stream = sst_hsw_stream_new(hsw, asoc_rtd_to_cpu(rtd, 0)->id, hsw_notify_pointer, pcm_data); if (pcm_data->stream == NULL) { dev_err(rtd->dev, "error: failed to create stream\n"); @@ -509,7 +509,7 @@ static int hsw_pcm_hw_params(struct snd_soc_component *component, path_id = SST_HSW_STREAM_PATH_SSP0_IN; /* DSP stream type depends on DAI ID */ - switch (rtd->cpu_dai->id) { + switch (asoc_rtd_to_cpu(rtd, 0)->id) { case 0: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { stream_type = SST_HSW_STREAM_TYPE_SYSTEM; @@ -533,7 +533,7 @@ static int hsw_pcm_hw_params(struct snd_soc_component *component, break; default: dev_err(rtd->dev, "error: invalid DAI ID %d\n", - rtd->cpu_dai->id); + asoc_rtd_to_cpu(rtd, 0)->id); return -EINVAL; } @@ -595,7 +595,7 @@ static int hsw_pcm_hw_params(struct snd_soc_component *component, dmab = snd_pcm_get_dma_buf(substream); ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area, - runtime->dma_bytes, rtd->cpu_dai->id); + runtime->dma_bytes, asoc_rtd_to_cpu(rtd, 0)->id); if (ret < 0) return ret; @@ -608,7 +608,7 @@ static int hsw_pcm_hw_params(struct snd_soc_component *component, pages = runtime->dma_bytes / PAGE_SIZE; ret = sst_hsw_stream_buffer(hsw, pcm_data->stream, - pdata->dmab[rtd->cpu_dai->id][substream->stream].addr, + pdata->dmab[asoc_rtd_to_cpu(rtd, 0)->id][substream->stream].addr, pages, runtime->dma_bytes, 0, snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT); if (ret < 0) { @@ -661,7 +661,7 @@ static int hsw_pcm_trigger(struct snd_soc_component *component, snd_pcm_uframes_t pos; int dai; - dai = mod_map[rtd->cpu_dai->id].dai_id; + dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; pcm_data = &pdata->pcm[dai][substream->stream]; sst_stream = pcm_data->stream; @@ -770,7 +770,7 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_soc_component *component, u32 position; int dai; - dai = mod_map[rtd->cpu_dai->id].dai_id; + dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; pcm_data = &pdata->pcm[dai][substream->stream]; position = sst_hsw_get_dsp_position(hsw, pcm_data->stream); @@ -791,7 +791,7 @@ static int hsw_pcm_open(struct snd_soc_component *component, struct sst_hsw *hsw = pdata->hsw; int dai; - dai = mod_map[rtd->cpu_dai->id].dai_id; + dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; pcm_data = &pdata->pcm[dai][substream->stream]; mutex_lock(&pcm_data->mutex); @@ -801,7 +801,7 @@ static int hsw_pcm_open(struct snd_soc_component *component, snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware); - pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id, + pcm_data->stream = sst_hsw_stream_new(hsw, asoc_rtd_to_cpu(rtd, 0)->id, hsw_notify_pointer, pcm_data); if (pcm_data->stream == NULL) { dev_err(rtd->dev, "error: failed to create stream\n"); @@ -824,7 +824,7 @@ static int hsw_pcm_close(struct snd_soc_component *component, struct sst_hsw *hsw = pdata->hsw; int ret, dai; - dai = mod_map[rtd->cpu_dai->id].dai_id; + dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; pcm_data = &pdata->pcm[dai][substream->stream]; mutex_lock(&pcm_data->mutex); @@ -923,9 +923,9 @@ static int hsw_pcm_new(struct snd_soc_component *component, hsw_pcm_hardware.buffer_bytes_max); } if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) - priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_PLAYBACK].hsw_pcm = pcm; + priv_data->pcm[asoc_rtd_to_cpu(rtd, 0)->id][SNDRV_PCM_STREAM_PLAYBACK].hsw_pcm = pcm; if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) - priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_CAPTURE].hsw_pcm = pcm; + priv_data->pcm[asoc_rtd_to_cpu(rtd, 0)->id][SNDRV_PCM_STREAM_CAPTURE].hsw_pcm = pcm; return 0; } diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 92a82e6b5fe6..38b9d7494083 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -17,7 +17,6 @@ #include "skl.h" #define BXT_BASEFW_TIMEOUT 3000 -#define BXT_INIT_TIMEOUT 300 #define BXT_ROM_INIT_TIMEOUT 70 #define BXT_IPC_PURGE_FW 0x01004000 @@ -38,8 +37,6 @@ /* Delay before scheduling D0i3 entry */ #define BXT_D0I3_DELAY 5000 -#define BXT_FW_ROM_INIT_RETRY 3 - static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) { return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 4f64f097e9ae..c6abcd5aa67b 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -57,18 +57,34 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) ctx->dsp_ops.stream_tag = stream_tag; memcpy(ctx->dmab.area, fwdata, fwsize); + ret = skl_dsp_core_power_up(ctx, SKL_DSP_CORE0_MASK); + if (ret < 0) { + dev_err(ctx->dev, "dsp core0 power up failed\n"); + ret = -EIO; + goto base_fw_load_failed; + } + /* purge FW request */ sst_dsp_shim_write(ctx, CNL_ADSP_REG_HIPCIDR, CNL_ADSP_REG_HIPCIDR_BUSY | (CNL_IPC_PURGE | ((stream_tag - 1) << CNL_ROM_CTRL_DMA_ID))); - ret = cnl_dsp_enable_core(ctx, SKL_DSP_CORE0_MASK); + ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK); if (ret < 0) { - dev_err(ctx->dev, "dsp boot core failed ret: %d\n", ret); + dev_err(ctx->dev, "Start dsp core failed ret: %d\n", ret); ret = -EIO; goto base_fw_load_failed; } + ret = sst_dsp_register_poll(ctx, CNL_ADSP_REG_HIPCIDA, + CNL_ADSP_REG_HIPCIDA_DONE, + CNL_ADSP_REG_HIPCIDA_DONE, + BXT_INIT_TIMEOUT, "HIPCIDA Done"); + if (ret < 0) { + dev_err(ctx->dev, "timeout for purge request: %d\n", ret); + goto base_fw_load_failed; + } + /* enable interrupt */ cnl_ipc_int_enable(ctx); cnl_ipc_op_int_enable(ctx); @@ -109,7 +125,7 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) { struct firmware stripped_fw; struct skl_dev *cnl = ctx->thread_context; - int ret; + int ret, i; if (!ctx->fw) { ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); @@ -131,12 +147,16 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) stripped_fw.size = ctx->fw->size; skl_dsp_strip_extended_manifest(&stripped_fw); - ret = cnl_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); - if (ret < 0) { - dev_err(ctx->dev, "prepare firmware failed: %d\n", ret); - goto cnl_load_base_firmware_failed; + for (i = 0; i < BXT_FW_ROM_INIT_RETRY; i++) { + ret = cnl_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); + if (!ret) + break; + dev_dbg(ctx->dev, "prepare firmware failed: %d\n", ret); } + if (ret < 0) + goto cnl_load_base_firmware_failed; + ret = sst_transfer_fw_host_dma(ctx); if (ret < 0) { dev_err(ctx->dev, "transfer firmware failed: %d\n", ret); @@ -158,6 +178,7 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) return 0; cnl_load_base_firmware_failed: + dev_err(ctx->dev, "firmware load failed: %d\n", ret); release_firmware(ctx->fw); ctx->fw = NULL; diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 19f328d71f24..d9c8f5cb389e 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -182,7 +182,8 @@ void skl_nhlt_remove_sysfs(struct skl_dev *skl) { struct device *dev = &skl->pci->dev; - sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr); + if (skl->nhlt) + sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr); } /* diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index b99509675d29..89dcccdfb1cd 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -112,10 +112,7 @@ static void skl_set_suspend_active(struct snd_pcm_substream *substream, struct snd_soc_dapm_widget *w; struct skl_dev *skl = bus_to_skl(bus); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + w = snd_soc_dai_get_widget(dai, substream->stream); if (w->ignore_suspend && enable) skl->supend_active++; @@ -475,10 +472,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, if (!mconfig) return -EIO; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + w = snd_soc_dai_get_widget(dai, substream->stream); switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: @@ -551,7 +545,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *link_dev; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct skl_pipe_params p_params = {0}; struct hdac_ext_link *link; int stream_tag; @@ -650,7 +644,7 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, link_dev->link_prepared = 0; - link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); + link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name); if (!link) return -EINVAL; @@ -1080,7 +1074,7 @@ static int skl_platform_soc_open(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai_link *dai_link = rtd->dai_link; - dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__, + dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "In %s:%s\n", __func__, dai_link->cpus->dai_name); snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw); @@ -1232,7 +1226,7 @@ static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream, u64 nsec) { struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); u64 codec_frames, codec_nsecs; if (!codec_dai->driver->ops->delay) @@ -1287,7 +1281,7 @@ static int skl_platform_soc_get_time_info( static int skl_platform_soc_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct snd_pcm *pcm = rtd->pcm; unsigned int size; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index cdfec0fca577..1df9ef422f61 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -67,6 +67,8 @@ struct skl_dev; #define SKL_FW_INIT 0x1 #define SKL_FW_RFW_START 0xf +#define BXT_FW_ROM_INIT_RETRY 3 +#define BXT_INIT_TIMEOUT 300 #define SKL_ADSPIC_IPC 1 #define SKL_ADSPIS_IPC 1 diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f755ca2484cf..63182bfd7941 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -130,6 +130,7 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) struct hdac_ext_link *hlink; int ret; + snd_hdac_set_codec_wakeup(bus, true); skl_enable_miscbdcge(bus->dev, false); ret = snd_hdac_bus_init_chip(bus, full_reset); @@ -138,6 +139,7 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); skl_enable_miscbdcge(bus->dev, true); + snd_hdac_set_codec_wakeup(bus, false); return ret; } @@ -359,7 +361,7 @@ static int skl_resume(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_bus *bus = pci_get_drvdata(pci); struct skl_dev *skl = bus_to_skl(bus); - struct hdac_ext_link *hlink = NULL; + struct hdac_ext_link *hlink; int ret; /* @@ -481,13 +483,8 @@ static struct skl_ssp_clk skl_ssp_clks[] = { static struct snd_soc_acpi_mach *skl_find_hda_machine(struct skl_dev *skl, struct snd_soc_acpi_mach *machines) { - struct hdac_bus *bus = skl_to_bus(skl); struct snd_soc_acpi_mach *mach; - /* check if we have any codecs detected on bus */ - if (bus->codec_mask == 0) - return NULL; - /* point to common table */ mach = snd_soc_acpi_intel_hda_machines; @@ -636,6 +633,9 @@ static int skl_clock_device_register(struct skl_dev *skl) struct platform_device_info pdevinfo = {NULL}; struct skl_clk_pdata *clk_pdata; + if (!skl->nhlt) + return 0; + clk_pdata = devm_kzalloc(&skl->pci->dev, sizeof(*clk_pdata), GFP_KERNEL); if (!clk_pdata) @@ -794,7 +794,7 @@ static void skl_probe_work(struct work_struct *work) { struct skl_dev *skl = container_of(work, struct skl_dev, probe_work); struct hdac_bus *bus = skl_to_bus(skl); - struct hdac_ext_link *hlink = NULL; + struct hdac_ext_link *hlink; int err; if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { @@ -803,6 +803,9 @@ static void skl_probe_work(struct work_struct *work) return; } + skl_init_pci(skl); + skl_dum_set(bus); + err = skl_init_chip(bus, true); if (err < 0) { dev_err(bus->dev, "Init chip failed with err: %d\n", err); @@ -918,8 +921,6 @@ static int skl_first_init(struct hdac_bus *bus) return -ENXIO; } - snd_hdac_bus_reset_link(bus, true); - snd_hdac_bus_parse_capabilities(bus); /* check if PPCAP exists */ @@ -967,11 +968,7 @@ static int skl_first_init(struct hdac_bus *bus) if (err < 0) return err; - /* initialize chip */ - skl_init_pci(skl); - skl_dum_set(bus); - - return skl_init_chip(bus, true); + return 0; } static int skl_probe(struct pci_dev *pci, @@ -1064,8 +1061,6 @@ static int skl_probe(struct pci_dev *pci, if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); - snd_hdac_bus_stop_chip(bus); - /* create device for soc dmic */ err = skl_dmic_device_register(skl); if (err < 0) { @@ -1082,7 +1077,8 @@ out_dsp_free: out_clk_free: skl_clock_device_unregister(skl); out_nhlt_free: - intel_nhlt_free(skl->nhlt); + if (skl->nhlt) + intel_nhlt_free(skl->nhlt); out_free: skl_free(bus); @@ -1131,7 +1127,8 @@ static void skl_remove(struct pci_dev *pci) skl_dmic_device_unregister(skl); skl_clock_device_unregister(skl); skl_nhlt_remove_sysfs(skl); - intel_nhlt_free(skl->nhlt); + if (skl->nhlt) + intel_nhlt_free(skl->nhlt); skl_free(bus); dev_set_drvdata(&pci->dev, NULL); } diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index 9d5405881209..6f6f8dad0356 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -49,12 +49,8 @@ #define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12 #define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8 -#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24 -#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16 -#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_MASK \ - (0xf << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) -#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_MASK \ - (0x1f << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) +#define JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24 +#define JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16 #define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19) #define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16) @@ -83,16 +79,23 @@ #define JZ_AIC_I2S_STATUS_BUSY BIT(2) #define JZ_AIC_CLK_DIV_MASK 0xf -#define I2SDIV_DV_SHIFT 8 +#define I2SDIV_DV_SHIFT 0 #define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT) #define I2SDIV_IDV_SHIFT 8 #define I2SDIV_IDV_MASK (0xf << I2SDIV_IDV_SHIFT) enum jz47xx_i2s_version { JZ_I2S_JZ4740, + JZ_I2S_JZ4760, + JZ_I2S_JZ4770, JZ_I2S_JZ4780, }; +struct i2s_soc_info { + enum jz47xx_i2s_version version; + struct snd_soc_dai_driver *dai; +}; + struct jz4740_i2s { struct resource *mem; void __iomem *base; @@ -104,7 +107,7 @@ struct jz4740_i2s { struct snd_dmaengine_dai_dma_data playback_dma_data; struct snd_dmaengine_dai_dma_data capture_dma_data; - enum jz47xx_i2s_version version; + const struct i2s_soc_info *soc_info; }; static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, @@ -284,7 +287,7 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; - if (i2s->version >= JZ_I2S_JZ4780) { + if (i2s->soc_info->version >= JZ_I2S_JZ4770) { div_reg &= ~I2SDIV_IDV_MASK; div_reg |= (div - 1) << I2SDIV_IDV_SHIFT; } else { @@ -398,9 +401,9 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, &i2s->capture_dma_data); - if (i2s->version >= JZ_I2S_JZ4780) { - conf = (7 << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | - (8 << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | + if (i2s->soc_info->version >= JZ_I2S_JZ4760) { + conf = (7 << JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | + (8 << JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | JZ_AIC_CONF_OVERFLOW_PLAY_LAST | JZ_AIC_CONF_I2S | JZ_AIC_CONF_INTERNAL_CODEC; @@ -457,7 +460,17 @@ static struct snd_soc_dai_driver jz4740_i2s_dai = { .ops = &jz4740_i2s_dai_ops, }; -static struct snd_soc_dai_driver jz4780_i2s_dai = { +static const struct i2s_soc_info jz4740_i2s_soc_info = { + .version = JZ_I2S_JZ4740, + .dai = &jz4740_i2s_dai, +}; + +static const struct i2s_soc_info jz4760_i2s_soc_info = { + .version = JZ_I2S_JZ4760, + .dai = &jz4740_i2s_dai, +}; + +static struct snd_soc_dai_driver jz4770_i2s_dai = { .probe = jz4740_i2s_dai_probe, .remove = jz4740_i2s_dai_remove, .playback = { @@ -475,6 +488,16 @@ static struct snd_soc_dai_driver jz4780_i2s_dai = { .ops = &jz4740_i2s_dai_ops, }; +static const struct i2s_soc_info jz4770_i2s_soc_info = { + .version = JZ_I2S_JZ4770, + .dai = &jz4770_i2s_dai, +}; + +static const struct i2s_soc_info jz4780_i2s_soc_info = { + .version = JZ_I2S_JZ4780, + .dai = &jz4770_i2s_dai, +}; + static const struct snd_soc_component_driver jz4740_i2s_component = { .name = "jz4740-i2s", .suspend = jz4740_i2s_suspend, @@ -483,8 +506,10 @@ static const struct snd_soc_component_driver jz4740_i2s_component = { #ifdef CONFIG_OF static const struct of_device_id jz4740_of_matches[] = { - { .compatible = "ingenic,jz4740-i2s", .data = (void *)JZ_I2S_JZ4740 }, - { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 }, + { .compatible = "ingenic,jz4740-i2s", .data = &jz4740_i2s_soc_info }, + { .compatible = "ingenic,jz4760-i2s", .data = &jz4760_i2s_soc_info }, + { .compatible = "ingenic,jz4770-i2s", .data = &jz4770_i2s_soc_info }, + { .compatible = "ingenic,jz4780-i2s", .data = &jz4780_i2s_soc_info }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, jz4740_of_matches); @@ -492,45 +517,40 @@ MODULE_DEVICE_TABLE(of, jz4740_of_matches); static int jz4740_i2s_dev_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct jz4740_i2s *i2s; struct resource *mem; int ret; - i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); + i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) return -ENOMEM; - i2s->version = - (enum jz47xx_i2s_version)of_device_get_match_data(&pdev->dev); + i2s->soc_info = device_get_match_data(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2s->base = devm_ioremap_resource(&pdev->dev, mem); + i2s->base = devm_ioremap_resource(dev, mem); if (IS_ERR(i2s->base)) return PTR_ERR(i2s->base); i2s->phys_base = mem->start; - i2s->clk_aic = devm_clk_get(&pdev->dev, "aic"); + i2s->clk_aic = devm_clk_get(dev, "aic"); if (IS_ERR(i2s->clk_aic)) return PTR_ERR(i2s->clk_aic); - i2s->clk_i2s = devm_clk_get(&pdev->dev, "i2s"); + i2s->clk_i2s = devm_clk_get(dev, "i2s"); if (IS_ERR(i2s->clk_i2s)) return PTR_ERR(i2s->clk_i2s); platform_set_drvdata(pdev, i2s); - if (i2s->version == JZ_I2S_JZ4780) - ret = devm_snd_soc_register_component(&pdev->dev, - &jz4740_i2s_component, &jz4780_i2s_dai, 1); - else - ret = devm_snd_soc_register_component(&pdev->dev, - &jz4740_i2s_component, &jz4740_i2s_dai, 1); - + ret = devm_snd_soc_register_component(dev, &jz4740_i2s_component, + i2s->soc_info->dai, 1); if (ret) return ret; - return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, + return devm_snd_dmaengine_pcm_register(dev, NULL, SND_DMAENGINE_PCM_FLAG_COMPAT); } diff --git a/sound/soc/kirkwood/armada-370-db.c b/sound/soc/kirkwood/armada-370-db.c index 8c3c808bda9a..4f66b011f1b4 100644 --- a/sound/soc/kirkwood/armada-370-db.c +++ b/sound/soc/kirkwood/armada-370-db.c @@ -19,7 +19,7 @@ static int a370db_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int freq; switch (params_rate(params)) { diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index f882b4003edf..e037826b2451 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -20,7 +20,7 @@ static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs) { struct snd_soc_pcm_runtime *soc_runtime = subs->private_data; - return snd_soc_dai_get_drvdata(soc_runtime->cpu_dai); + return snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(soc_runtime, 0)); } static const struct snd_pcm_hardware kirkwood_dma_snd_hw = { diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c index 4254f3a954dd..375e3b492922 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c @@ -40,7 +40,7 @@ int mtk_afe_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct snd_pcm_runtime *runtime = substream->runtime; - int memif_num = rtd->cpu_dai->id; + int memif_num = asoc_rtd_to_cpu(rtd, 0)->id; struct mtk_base_afe_memif *memif = &afe->memif[memif_num]; const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware; int ret; @@ -100,7 +100,7 @@ void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + struct mtk_base_afe_memif *memif = &afe->memif[asoc_rtd_to_cpu(rtd, 0)->id]; int irq_id; irq_id = memif->irq_usage; @@ -122,7 +122,7 @@ int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - int id = rtd->cpu_dai->id; + int id = asoc_rtd_to_cpu(rtd, 0)->id; struct mtk_base_afe_memif *memif = &afe->memif[id]; int ret; unsigned int channels = params_channels(params); @@ -199,7 +199,7 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime * const runtime = substream->runtime; struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - int id = rtd->cpu_dai->id; + int id = asoc_rtd_to_cpu(rtd, 0)->id; struct mtk_base_afe_memif *memif = &afe->memif[id]; struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage]; const struct mtk_base_irq_data *irq_data = irqs->irq_data; @@ -265,7 +265,7 @@ int mtk_afe_fe_prepare(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - int id = rtd->cpu_dai->id; + int id = asoc_rtd_to_cpu(rtd, 0)->id; int pbuf_size; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index 44dfef713905..0a1a65c86f0e 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -82,7 +82,7 @@ snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); - struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + struct mtk_base_afe_memif *memif = &afe->memif[asoc_rtd_to_cpu(rtd, 0)->id]; const struct mtk_base_memif_data *memif_data = memif->data; struct regmap *regmap = afe->regmap; struct device *dev = afe->dev; diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index 488603a0c4b1..f0250b0dd734 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -497,7 +497,7 @@ static int mt2701_memif_fs(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; int fs; - if (rtd->cpu_dai->id != MT2701_MEMIF_ULBT) + if (asoc_rtd_to_cpu(rtd, 0)->id != MT2701_MEMIF_ULBT) fs = mt2701_afe_i2s_fs(rate); else fs = (rate == 16000 ? 1 : 0); diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c index b6941796efca..c47af9b6949b 100644 --- a/sound/soc/mediatek/mt2701/mt2701-cs42448.c +++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c @@ -128,8 +128,8 @@ static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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 mclk_rate; unsigned int rate = params_rate(params); unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4; diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c index 8c4c89e4c616..0122e7df067f 100644 --- a/sound/soc/mediatek/mt2701/mt2701-wm8960.c +++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c @@ -25,8 +25,8 @@ static int mt2701_wm8960_be_ops_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int mclk_rate; unsigned int rate = params_rate(params); unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4; diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 378bfc16ef52..7f930556d961 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -143,7 +143,7 @@ static int mt6797_memif_fs(struct snd_pcm_substream *substream, struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); - int id = rtd->cpu_dai->id; + int id = asoc_rtd_to_cpu(rtd, 0)->id; return mt6797_rate_transform(afe->dev, rate, id); } diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index 461e4de8c918..1e3f2d786066 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -485,7 +485,7 @@ static int mt8173_memif_fs(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); - struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + struct mtk_base_afe_memif *memif = &afe->memif[asoc_rtd_to_cpu(rtd, 0)->id]; int fs; if (memif->data->id == MT8173_AFE_MEMIF_DAI || diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index 22c00600c999..37693d354e66 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -53,7 +53,7 @@ static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, SND_SOC_CLOCK_IN); @@ -67,7 +67,7 @@ static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) { int ret; struct snd_soc_card *card = runtime->card; - struct snd_soc_component *component = runtime->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; /* enable jack detection */ ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 2e1e61d8f127..51009a172777 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -47,7 +47,7 @@ static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; int i, ret; - for_each_rtd_codec_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { /* pll from mclk 12.288M */ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, params_rate(params) * 512); @@ -73,7 +73,7 @@ static struct snd_soc_jack mt8173_rt5650_rt5514_jack; static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; - struct snd_soc_component *component = runtime->codec_dais[0]->component; + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; int ret; rt5645_sel_asrc_clk_src(component, diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index ebcc0b86286b..247ac7690805 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -51,7 +51,7 @@ static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; int i, ret; - for_each_rtd_codec_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { /* pll from mclk 12.288M */ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, params_rate(params) * 512); @@ -77,8 +77,8 @@ static struct snd_soc_jack mt8173_rt5650_rt5676_jack; static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; - struct snd_soc_component *component = runtime->codec_dais[0]->component; - struct snd_soc_component *component_sub = runtime->codec_dais[1]->component; + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; + struct snd_soc_component *component_sub = asoc_rtd_to_codec(runtime, 1)->component; int ret; rt5645_sel_asrc_clk_src(component, diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index ef6f23675286..2065c94dbf99 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -11,6 +11,7 @@ #include <linux/of_gpio.h> #include <sound/soc.h> #include <sound/jack.h> +#include <sound/hdmi-codec.h> #include "../../codecs/rt5645.h" #define MCLK_FOR_CODECS 12288000 @@ -77,7 +78,7 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream, break; } - for_each_rtd_codec_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { /* pll from mclk */ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock, params_rate(params) * 512); @@ -98,13 +99,13 @@ static const struct snd_soc_ops mt8173_rt5650_ops = { .hw_params = mt8173_rt5650_hw_params, }; -static struct snd_soc_jack mt8173_rt5650_jack; +static struct snd_soc_jack mt8173_rt5650_jack, mt8173_rt5650_hdmi_jack; static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; - struct snd_soc_component *component = runtime->codec_dais[0]->component; - const char *codec_capture_dai = runtime->codec_dais[1]->name; + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; + const char *codec_capture_dai = asoc_rtd_to_codec(runtime, 1)->name; int ret; rt5645_sel_asrc_clk_src(component, @@ -144,6 +145,19 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime) &mt8173_rt5650_jack); } +static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, + &mt8173_rt5650_hdmi_jack, NULL, 0); + if (ret) + return ret; + + return hdmi_codec_set_jack_detect(asoc_rtd_to_codec(rtd, 0)->component, + &mt8173_rt5650_hdmi_jack); +} + enum { DAI_LINK_PLAYBACK, DAI_LINK_CAPTURE, @@ -222,6 +236,7 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = { .name = "HDMI BE", .no_pcm = 1, .dpcm_playback = 1, + .init = mt8173_rt5650_hdmi_init, SND_SOC_DAILINK_REG(hdmi_be), }, }; diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 6e2270bbb10e..c8ded53bde1d 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -146,7 +146,7 @@ static int mt8183_memif_fs(struct snd_pcm_substream *substream, struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); - int id = rtd->cpu_dai->id; + int id = asoc_rtd_to_cpu(rtd, 0)->id; return mt8183_rate_transform(afe->dev, rate, id); } diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index c65493721e90..5b3dfa79b4ae 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -16,7 +16,9 @@ #include "../../codecs/da7219-aad.h" #include "../../codecs/da7219.h" -static struct snd_soc_jack headset_jack; +struct mt8183_da7219_max98357_priv { + struct snd_soc_jack headset_jack; +}; static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -26,7 +28,7 @@ static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream, unsigned int mclk_fs_ratio = 128; unsigned int mclk_fs = rate * mclk_fs_ratio; - return snd_soc_dai_set_sysclk(rtd->cpu_dai, + return snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk_fs, SND_SOC_CLOCK_OUT); } @@ -38,19 +40,19 @@ static int mt8183_da7219_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; unsigned int rate = params_rate(params); unsigned int mclk_fs_ratio = 256; unsigned int mclk_fs = rate * mclk_fs_ratio; unsigned int freq; int ret = 0, j; - ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk_fs, SND_SOC_CLOCK_OUT); if (ret < 0) dev_err(rtd->dev, "failed to set cpu dai sysclk\n"); - for (j = 0; j < rtd->num_codecs; j++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + for_each_rtd_codec_dais(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name, "da7219.5-001a")) { ret = snd_soc_dai_set_sysclk(codec_dai, @@ -80,10 +82,10 @@ static int mt8183_da7219_i2s_hw_params(struct snd_pcm_substream *substream, static int mt8183_da7219_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; int ret = 0, j; - for (j = 0; j < rtd->num_codecs; j++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + for_each_rtd_codec_dais(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name, "da7219.5-001a")) { ret = snd_soc_dai_set_pll(codec_dai, @@ -116,6 +118,46 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int +mt8183_da7219_max98357_bt_sco_startup( + struct snd_pcm_substream *substream) +{ + static const unsigned int rates[] = { + 8000, 16000 + }; + static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, + }; + static const unsigned int channels[] = { + 1, + }; + static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, + }; + + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + runtime->hw.channels_max = 1; + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + return 0; +} + +static const struct snd_soc_ops mt8183_da7219_max98357_bt_sco_ops = { + .startup = mt8183_da7219_max98357_bt_sco_startup, +}; + /* FE */ SND_SOC_DAILINK_DEFS(playback1, DAILINK_COMP_ARRAY(COMP_CPU("DL1")), @@ -222,6 +264,7 @@ static struct snd_soc_dai_link mt8183_da7219_max98357_dai_links[] = { SND_SOC_DPCM_TRIGGER_PRE}, .dynamic = 1, .dpcm_playback = 1, + .ops = &mt8183_da7219_max98357_bt_sco_ops, SND_SOC_DAILINK_REG(playback2), }, { @@ -240,6 +283,7 @@ static struct snd_soc_dai_link mt8183_da7219_max98357_dai_links[] = { SND_SOC_DPCM_TRIGGER_PRE}, .dynamic = 1, .dpcm_capture = 1, + .ops = &mt8183_da7219_max98357_bt_sco_ops, SND_SOC_DAILINK_REG(capture1), }, { @@ -351,8 +395,12 @@ static struct snd_soc_dai_link mt8183_da7219_max98357_dai_links[] = { { .name = "TDM", .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_IB_IF | + SND_SOC_DAIFMT_CBM_CFM, .dpcm_playback = 1, .ignore_suspend = 1, + .be_hw_params_fixup = mt8183_i2s_hw_params_fixup, SND_SOC_DAILINK_REG(tdm), }, }; @@ -372,9 +420,31 @@ static struct snd_soc_codec_conf mt6358_codec_conf[] = { }, }; +static const struct snd_kcontrol_new mt8183_da7219_max98357_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Speakers"), +}; + +static const +struct snd_soc_dapm_widget mt8183_da7219_max98357_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_PINCTRL("TDM_OUT_PINCTRL", + "aud_tdm_out_on", "aud_tdm_out_off"), +}; + +static const struct snd_soc_dapm_route mt8183_da7219_max98357_dapm_routes[] = { + {"Speakers", NULL, "Speaker"}, + {"I2S Playback", NULL, "TDM_OUT_PINCTRL"}, +}; + static struct snd_soc_card mt8183_da7219_max98357_card = { .name = "mt8183_da7219_max98357", .owner = THIS_MODULE, + .controls = mt8183_da7219_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_da7219_max98357_snd_controls), + .dapm_widgets = mt8183_da7219_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_da7219_max98357_dapm_widgets), + .dapm_routes = mt8183_da7219_max98357_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt8183_da7219_max98357_dapm_routes), .dai_link = mt8183_da7219_max98357_dai_links, .num_links = ARRAY_SIZE(mt8183_da7219_max98357_dai_links), .aux_dev = &mt8183_da7219_max98357_headset_dev, @@ -387,6 +457,8 @@ static int mt8183_da7219_max98357_headset_init(struct snd_soc_component *component) { int ret; + struct mt8183_da7219_max98357_priv *priv = + snd_soc_card_get_drvdata(component->card); /* Enable Headset and 4 Buttons Jack detection */ ret = snd_soc_card_jack_new(&mt8183_da7219_max98357_card, @@ -394,12 +466,12 @@ mt8183_da7219_max98357_headset_init(struct snd_soc_component *component) SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &headset_jack, + &priv->headset_jack, NULL, 0); if (ret) return ret; - da7219_aad_jack_det(component, &headset_jack); + da7219_aad_jack_det(component, &priv->headset_jack); return ret; } @@ -409,7 +481,8 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) struct snd_soc_card *card = &mt8183_da7219_max98357_card; struct device_node *platform_node; struct snd_soc_dai_link *dai_link; - struct pinctrl *default_pins; + struct mt8183_da7219_max98357_priv *priv; + struct pinctrl *pinctrl; int ret, i; card->dev = &pdev->dev; @@ -436,22 +509,21 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) return -EINVAL; } - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, priv); + + pinctrl = devm_pinctrl_get_select(&pdev->dev, PINCTRL_STATE_DEFAULT); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + dev_err(&pdev->dev, "%s failed to select default state %d\n", __func__, ret); return ret; } - default_pins = - devm_pinctrl_get_select(&pdev->dev, PINCTRL_STATE_DEFAULT); - if (IS_ERR(default_pins)) { - dev_err(&pdev->dev, "%s set pins failed\n", - __func__); - return PTR_ERR(default_pins); - } - - return ret; + return devm_snd_soc_register_card(&pdev->dev, card); } #ifdef CONFIG_OF @@ -478,4 +550,3 @@ MODULE_DESCRIPTION("MT8183-DA7219-MAX98357 ALSA SoC machine driver"); MODULE_AUTHOR("Shunli Wang <shunli.wang@mediatek.com>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("mt8183_da7219_max98357 soc card"); - diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index 0555f7d73d05..1fca8df109b4 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -41,7 +41,7 @@ static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream, unsigned int mclk_fs_ratio = 128; unsigned int mclk_fs = rate * mclk_fs_ratio; - return snd_soc_dai_set_sysclk(rtd->cpu_dai, + return snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk_fs, SND_SOC_CLOCK_OUT); } diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig index 2e3676147cea..8b6295283989 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig @@ -2,6 +2,16 @@ menu "ASoC support for Amlogic platforms" depends on ARCH_MESON || COMPILE_TEST +config SND_MESON_AIU + tristate "Amlogic AIU" + select SND_MESON_CODEC_GLUE + select SND_PCM_IEC958 + imply SND_SOC_MESON_T9015 + imply SND_SOC_HDMI_CODEC if DRM_MESON_DW_HDMI + help + Select Y or M to add support for the Audio output subsystem found + in the Amlogic Meson8, Meson8b and GX SoC families + config SND_MESON_AXG_FIFO tristate select REGMAP_MMIO @@ -50,6 +60,7 @@ config SND_MESON_AXG_TDMOUT config SND_MESON_AXG_SOUND_CARD tristate "Amlogic AXG Sound Card Support" select SND_MESON_AXG_TDM_INTERFACE + select SND_MESON_CARD_UTILS imply SND_MESON_AXG_FRDDR imply SND_MESON_AXG_TODDR imply SND_MESON_AXG_TDMIN @@ -85,11 +96,41 @@ config SND_MESON_AXG_PDM Select Y or M to add support for PDM input embedded in the Amlogic AXG SoC family +config SND_MESON_CARD_UTILS + tristate + +config SND_MESON_CODEC_GLUE + tristate + +config SND_MESON_GX_SOUND_CARD + tristate "Amlogic GX Sound Card Support" + select SND_MESON_CARD_UTILS + imply SND_MESON_AIU + help + Select Y or M to add support for the GXBB/GXL SoC sound card + +config SND_MESON_G12A_TOACODEC + tristate "Amlogic G12A To Internal DAC Control Support" + select SND_MESON_CODEC_GLUE + select REGMAP_MMIO + imply SND_SOC_MESON_T9015 + help + Select Y or M to add support for the internal audio DAC on the + g12a SoC family + config SND_MESON_G12A_TOHDMITX tristate "Amlogic G12A To HDMI TX Control Support" select REGMAP_MMIO + select SND_MESON_CODEC_GLUE imply SND_SOC_HDMI_CODEC help Select Y or M to add support for HDMI audio on the g12a SoC family + +config SND_SOC_MESON_T9015 + tristate "Amlogic T9015 DAC" + select REGMAP_MMIO + help + Say Y or M if you want to add support for the internal DAC found + on GXL, G12 and SM1 SoC family. endmenu diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile index 1a8b1470ed84..e446bc980481 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile @@ -1,5 +1,13 @@ # SPDX-License-Identifier: (GPL-2.0 OR MIT) +snd-soc-meson-aiu-objs := aiu.o +snd-soc-meson-aiu-objs += aiu-acodec-ctrl.o +snd-soc-meson-aiu-objs += aiu-codec-ctrl.o +snd-soc-meson-aiu-objs += aiu-encoder-i2s.o +snd-soc-meson-aiu-objs += aiu-encoder-spdif.o +snd-soc-meson-aiu-objs += aiu-fifo.o +snd-soc-meson-aiu-objs += aiu-fifo-i2s.o +snd-soc-meson-aiu-objs += aiu-fifo-spdif.o snd-soc-meson-axg-fifo-objs := axg-fifo.o snd-soc-meson-axg-frddr-objs := axg-frddr.o snd-soc-meson-axg-toddr-objs := axg-toddr.o @@ -11,8 +19,14 @@ snd-soc-meson-axg-sound-card-objs := axg-card.o snd-soc-meson-axg-spdifin-objs := axg-spdifin.o snd-soc-meson-axg-spdifout-objs := axg-spdifout.o snd-soc-meson-axg-pdm-objs := axg-pdm.o +snd-soc-meson-card-utils-objs := meson-card-utils.o +snd-soc-meson-codec-glue-objs := meson-codec-glue.o +snd-soc-meson-gx-sound-card-objs := gx-card.o +snd-soc-meson-g12a-toacodec-objs := g12a-toacodec.o snd-soc-meson-g12a-tohdmitx-objs := g12a-tohdmitx.o +snd-soc-meson-t9015-objs := t9015.o +obj-$(CONFIG_SND_MESON_AIU) += snd-soc-meson-aiu.o obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o @@ -24,4 +38,9 @@ obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o obj-$(CONFIG_SND_MESON_AXG_SPDIFIN) += snd-soc-meson-axg-spdifin.o obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o obj-$(CONFIG_SND_MESON_AXG_PDM) += snd-soc-meson-axg-pdm.o +obj-$(CONFIG_SND_MESON_CARD_UTILS) += snd-soc-meson-card-utils.o +obj-$(CONFIG_SND_MESON_CODEC_GLUE) += snd-soc-meson-codec-glue.o +obj-$(CONFIG_SND_MESON_GX_SOUND_CARD) += snd-soc-meson-gx-sound-card.o +obj-$(CONFIG_SND_MESON_G12A_TOACODEC) += snd-soc-meson-g12a-toacodec.o obj-$(CONFIG_SND_MESON_G12A_TOHDMITX) += snd-soc-meson-g12a-tohdmitx.o +obj-$(CONFIG_SND_SOC_MESON_T9015) += snd-soc-meson-t9015.o diff --git a/sound/soc/meson/aiu-acodec-ctrl.c b/sound/soc/meson/aiu-acodec-ctrl.c new file mode 100644 index 000000000000..7078197e0cc5 --- /dev/null +++ b/sound/soc/meson/aiu-acodec-ctrl.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include <dt-bindings/sound/meson-aiu.h> +#include "aiu.h" +#include "meson-codec-glue.h" + +#define CTRL_DIN_EN 15 +#define CTRL_CLK_INV BIT(14) +#define CTRL_LRCLK_INV BIT(13) +#define CTRL_I2S_IN_BCLK_SRC BIT(11) +#define CTRL_DIN_LRCLK_SRC_SHIFT 6 +#define CTRL_DIN_LRCLK_SRC (0x3 << CTRL_DIN_LRCLK_SRC_SHIFT) +#define CTRL_BCLK_MCLK_SRC GENMASK(5, 4) +#define CTRL_DIN_SKEW GENMASK(3, 2) +#define CTRL_I2S_OUT_LANE_SRC 0 + +#define AIU_ACODEC_OUT_CHMAX 2 + +static const char * const aiu_acodec_ctrl_mux_texts[] = { + "DISABLED", "I2S", "PCM", +}; + +static int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, changed; + + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); + changed = snd_soc_component_test_bits(component, e->reg, + CTRL_DIN_LRCLK_SRC, + FIELD_PREP(CTRL_DIN_LRCLK_SRC, + mux)); + + if (!changed) + return 0; + + /* Force disconnect of the mux while updating */ + snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); + + snd_soc_component_update_bits(component, e->reg, + CTRL_DIN_LRCLK_SRC | + CTRL_BCLK_MCLK_SRC, + FIELD_PREP(CTRL_DIN_LRCLK_SRC, mux) | + FIELD_PREP(CTRL_BCLK_MCLK_SRC, mux)); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); + + return 0; +} + +static SOC_ENUM_SINGLE_DECL(aiu_acodec_ctrl_mux_enum, AIU_ACODEC_CTRL, + CTRL_DIN_LRCLK_SRC_SHIFT, + aiu_acodec_ctrl_mux_texts); + +static const struct snd_kcontrol_new aiu_acodec_ctrl_mux = + SOC_DAPM_ENUM_EXT("ACodec Source", aiu_acodec_ctrl_mux_enum, + snd_soc_dapm_get_enum_double, + aiu_acodec_ctrl_mux_put_enum); + +static const struct snd_kcontrol_new aiu_acodec_ctrl_out_enable = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", AIU_ACODEC_CTRL, + CTRL_DIN_EN, 1, 0); + +static const struct snd_soc_dapm_widget aiu_acodec_ctrl_widgets[] = { + SND_SOC_DAPM_MUX("ACODEC SRC", SND_SOC_NOPM, 0, 0, + &aiu_acodec_ctrl_mux), + SND_SOC_DAPM_SWITCH("ACODEC OUT EN", SND_SOC_NOPM, 0, 0, + &aiu_acodec_ctrl_out_enable), +}; + +static int aiu_acodec_ctrl_input_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct meson_codec_glue_input *data; + int ret; + + ret = meson_codec_glue_input_hw_params(substream, params, dai); + if (ret) + return ret; + + /* The glue will provide 1 lane out of the 4 to the output */ + data = meson_codec_glue_input_get_data(dai); + data->params.channels_min = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX, + data->params.channels_min); + data->params.channels_max = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX, + data->params.channels_max); + + return 0; +} + +static const struct snd_soc_dai_ops aiu_acodec_ctrl_input_ops = { + .hw_params = aiu_acodec_ctrl_input_hw_params, + .set_fmt = meson_codec_glue_input_set_fmt, +}; + +static const struct snd_soc_dai_ops aiu_acodec_ctrl_output_ops = { + .startup = meson_codec_glue_output_startup, +}; + +#define AIU_ACODEC_CTRL_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AIU_ACODEC_STREAM(xname, xsuffix, xchmax) \ +{ \ + .stream_name = xname " " xsuffix, \ + .channels_min = 1, \ + .channels_max = (xchmax), \ + .rate_min = 5512, \ + .rate_max = 192000, \ + .formats = AIU_ACODEC_CTRL_FORMATS, \ +} + +#define AIU_ACODEC_INPUT(xname) { \ + .name = "ACODEC CTRL " xname, \ + .playback = AIU_ACODEC_STREAM(xname, "Playback", 8), \ + .ops = &aiu_acodec_ctrl_input_ops, \ + .probe = meson_codec_glue_input_dai_probe, \ + .remove = meson_codec_glue_input_dai_remove, \ +} + +#define AIU_ACODEC_OUTPUT(xname) { \ + .name = "ACODEC CTRL " xname, \ + .capture = AIU_ACODEC_STREAM(xname, "Capture", AIU_ACODEC_OUT_CHMAX), \ + .ops = &aiu_acodec_ctrl_output_ops, \ +} + +static struct snd_soc_dai_driver aiu_acodec_ctrl_dai_drv[] = { + [CTRL_I2S] = AIU_ACODEC_INPUT("ACODEC I2S IN"), + [CTRL_PCM] = AIU_ACODEC_INPUT("ACODEC PCM IN"), + [CTRL_OUT] = AIU_ACODEC_OUTPUT("ACODEC OUT"), +}; + +static const struct snd_soc_dapm_route aiu_acodec_ctrl_routes[] = { + { "ACODEC SRC", "I2S", "ACODEC I2S IN Playback" }, + { "ACODEC SRC", "PCM", "ACODEC PCM IN Playback" }, + { "ACODEC OUT EN", "Switch", "ACODEC SRC" }, + { "ACODEC OUT Capture", NULL, "ACODEC OUT EN" }, +}; + +static const struct snd_kcontrol_new aiu_acodec_ctrl_controls[] = { + SOC_SINGLE("ACODEC I2S Lane Select", AIU_ACODEC_CTRL, + CTRL_I2S_OUT_LANE_SRC, 3, 0), +}; + +static int aiu_acodec_of_xlate_dai_name(struct snd_soc_component *component, + struct of_phandle_args *args, + const char **dai_name) +{ + return aiu_of_xlate_dai_name(component, args, dai_name, AIU_ACODEC); +} + +static int aiu_acodec_ctrl_component_probe(struct snd_soc_component *component) +{ + /* + * NOTE: Din Skew setting + * According to the documentation, the following update adds one delay + * to the din line. Without this, the output saturates. This happens + * regardless of the link format (i2s or left_j) so it is not clear what + * it actually does but it seems to be required + */ + snd_soc_component_update_bits(component, AIU_ACODEC_CTRL, + CTRL_DIN_SKEW, + FIELD_PREP(CTRL_DIN_SKEW, 2)); + + return 0; +} + +static const struct snd_soc_component_driver aiu_acodec_ctrl_component = { + .name = "AIU Internal DAC Codec Control", + .probe = aiu_acodec_ctrl_component_probe, + .controls = aiu_acodec_ctrl_controls, + .num_controls = ARRAY_SIZE(aiu_acodec_ctrl_controls), + .dapm_widgets = aiu_acodec_ctrl_widgets, + .num_dapm_widgets = ARRAY_SIZE(aiu_acodec_ctrl_widgets), + .dapm_routes = aiu_acodec_ctrl_routes, + .num_dapm_routes = ARRAY_SIZE(aiu_acodec_ctrl_routes), + .of_xlate_dai_name = aiu_acodec_of_xlate_dai_name, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +int aiu_acodec_ctrl_register_component(struct device *dev) +{ + return snd_soc_register_component(dev, &aiu_acodec_ctrl_component, + aiu_acodec_ctrl_dai_drv, + ARRAY_SIZE(aiu_acodec_ctrl_dai_drv)); +} diff --git a/sound/soc/meson/aiu-codec-ctrl.c b/sound/soc/meson/aiu-codec-ctrl.c new file mode 100644 index 000000000000..4b773d3e8b07 --- /dev/null +++ b/sound/soc/meson/aiu-codec-ctrl.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include <dt-bindings/sound/meson-aiu.h> +#include "aiu.h" +#include "meson-codec-glue.h" + +#define CTRL_CLK_SEL GENMASK(1, 0) +#define CTRL_DATA_SEL_SHIFT 4 +#define CTRL_DATA_SEL (0x3 << CTRL_DATA_SEL_SHIFT) + +static const char * const aiu_codec_ctrl_mux_texts[] = { + "DISABLED", "PCM", "I2S", +}; + +static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, changed; + + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); + changed = snd_soc_component_test_bits(component, e->reg, + CTRL_DATA_SEL, + FIELD_PREP(CTRL_DATA_SEL, mux)); + + if (!changed) + return 0; + + /* Force disconnect of the mux while updating */ + snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); + + /* Reset the source first */ + snd_soc_component_update_bits(component, e->reg, + CTRL_CLK_SEL | + CTRL_DATA_SEL, + FIELD_PREP(CTRL_CLK_SEL, 0) | + FIELD_PREP(CTRL_DATA_SEL, 0)); + + /* Set the appropriate source */ + snd_soc_component_update_bits(component, e->reg, + CTRL_CLK_SEL | + CTRL_DATA_SEL, + FIELD_PREP(CTRL_CLK_SEL, mux) | + FIELD_PREP(CTRL_DATA_SEL, mux)); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); + + return 0; +} + +static SOC_ENUM_SINGLE_DECL(aiu_hdmi_ctrl_mux_enum, AIU_HDMI_CLK_DATA_CTRL, + CTRL_DATA_SEL_SHIFT, + aiu_codec_ctrl_mux_texts); + +static const struct snd_kcontrol_new aiu_hdmi_ctrl_mux = + SOC_DAPM_ENUM_EXT("HDMI Source", aiu_hdmi_ctrl_mux_enum, + snd_soc_dapm_get_enum_double, + aiu_codec_ctrl_mux_put_enum); + +static const struct snd_soc_dapm_widget aiu_hdmi_ctrl_widgets[] = { + SND_SOC_DAPM_MUX("HDMI CTRL SRC", SND_SOC_NOPM, 0, 0, + &aiu_hdmi_ctrl_mux), +}; + +static const struct snd_soc_dai_ops aiu_codec_ctrl_input_ops = { + .hw_params = meson_codec_glue_input_hw_params, + .set_fmt = meson_codec_glue_input_set_fmt, +}; + +static const struct snd_soc_dai_ops aiu_codec_ctrl_output_ops = { + .startup = meson_codec_glue_output_startup, +}; + +#define AIU_CODEC_CTRL_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AIU_CODEC_CTRL_STREAM(xname, xsuffix) \ +{ \ + .stream_name = xname " " xsuffix, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 5512, \ + .rate_max = 192000, \ + .formats = AIU_CODEC_CTRL_FORMATS, \ +} + +#define AIU_CODEC_CTRL_INPUT(xname) { \ + .name = "CODEC CTRL " xname, \ + .playback = AIU_CODEC_CTRL_STREAM(xname, "Playback"), \ + .ops = &aiu_codec_ctrl_input_ops, \ + .probe = meson_codec_glue_input_dai_probe, \ + .remove = meson_codec_glue_input_dai_remove, \ +} + +#define AIU_CODEC_CTRL_OUTPUT(xname) { \ + .name = "CODEC CTRL " xname, \ + .capture = AIU_CODEC_CTRL_STREAM(xname, "Capture"), \ + .ops = &aiu_codec_ctrl_output_ops, \ +} + +static struct snd_soc_dai_driver aiu_hdmi_ctrl_dai_drv[] = { + [CTRL_I2S] = AIU_CODEC_CTRL_INPUT("HDMI I2S IN"), + [CTRL_PCM] = AIU_CODEC_CTRL_INPUT("HDMI PCM IN"), + [CTRL_OUT] = AIU_CODEC_CTRL_OUTPUT("HDMI OUT"), +}; + +static const struct snd_soc_dapm_route aiu_hdmi_ctrl_routes[] = { + { "HDMI CTRL SRC", "I2S", "HDMI I2S IN Playback" }, + { "HDMI CTRL SRC", "PCM", "HDMI PCM IN Playback" }, + { "HDMI OUT Capture", NULL, "HDMI CTRL SRC" }, +}; + +static int aiu_hdmi_of_xlate_dai_name(struct snd_soc_component *component, + struct of_phandle_args *args, + const char **dai_name) +{ + return aiu_of_xlate_dai_name(component, args, dai_name, AIU_HDMI); +} + +static const struct snd_soc_component_driver aiu_hdmi_ctrl_component = { + .name = "AIU HDMI Codec Control", + .dapm_widgets = aiu_hdmi_ctrl_widgets, + .num_dapm_widgets = ARRAY_SIZE(aiu_hdmi_ctrl_widgets), + .dapm_routes = aiu_hdmi_ctrl_routes, + .num_dapm_routes = ARRAY_SIZE(aiu_hdmi_ctrl_routes), + .of_xlate_dai_name = aiu_hdmi_of_xlate_dai_name, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +int aiu_hdmi_ctrl_register_component(struct device *dev) +{ + return snd_soc_register_component(dev, &aiu_hdmi_ctrl_component, + aiu_hdmi_ctrl_dai_drv, + ARRAY_SIZE(aiu_hdmi_ctrl_dai_drv)); +} + diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c new file mode 100644 index 000000000000..832e22d275fe --- /dev/null +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "aiu.h" + +#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) +#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) +#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) +#define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) +#define AIU_RST_SOFT_I2S_FAST BIT(0) + +#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) +#define AIU_I2S_MISC_HOLD_EN BIT(2) +#define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) +#define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) +#define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) +#define AIU_CLK_CTRL_LRCLK_INVERT BIT(7) +#define AIU_CLK_CTRL_LRCLK_SKEW GENMASK(9, 8) +#define AIU_CLK_CTRL_MORE_HDMI_AMCLK BIT(6) +#define AIU_CLK_CTRL_MORE_I2S_DIV GENMASK(5, 0) +#define AIU_CODEC_DAC_LRCLK_CTRL_DIV GENMASK(11, 0) + +static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_update_bits(component, AIU_CLK_CTRL, + AIU_CLK_CTRL_I2S_DIV_EN, + enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); +} + +static void aiu_encoder_i2s_hold(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_update_bits(component, AIU_I2S_MISC, + AIU_I2S_MISC_HOLD_EN, + enable ? AIU_I2S_MISC_HOLD_EN : 0); +} + +static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + aiu_encoder_i2s_hold(component, false); + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + aiu_encoder_i2s_hold(component, true); + return 0; + + default: + return -EINVAL; + } +} + +static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + /* Always operate in split (classic interleaved) mode */ + unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; + unsigned int val; + + /* Reset required to update the pipeline */ + snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); + snd_soc_component_read(component, AIU_I2S_SYNC, &val); + + switch (params_physical_width(params)) { + case 16: /* Nothing to do */ + break; + + case 32: + desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | + AIU_I2S_SOURCE_DESC_MODE_32BIT); + break; + + default: + return -EINVAL; + } + + switch (params_channels(params)) { + case 2: /* Nothing to do */ + break; + case 8: + desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, + AIU_I2S_SOURCE_DESC_MODE_8CH | + AIU_I2S_SOURCE_DESC_MODE_24BIT | + AIU_I2S_SOURCE_DESC_MODE_32BIT | + AIU_I2S_SOURCE_DESC_MODE_SPLIT, + desc); + + return 0; +} + +static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, + struct snd_pcm_hw_params *params, + unsigned int bs) +{ + switch (bs) { + case 1: + case 2: + case 4: + case 8: + /* These are the only valid legacy dividers */ + break; + + default: + dev_err(component->dev, "Unsupported i2s divider: %u\n", bs); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AIU_CLK_CTRL, + AIU_CLK_CTRL_I2S_DIV, + FIELD_PREP(AIU_CLK_CTRL_I2S_DIV, + __ffs(bs))); + + snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, + AIU_CLK_CTRL_MORE_I2S_DIV, + FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV, + 0)); + + return 0; +} + +static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, + struct snd_pcm_hw_params *params, + unsigned int bs) +{ + /* + * NOTE: this HW is odd. + * In most configuration, the i2s divider is 'mclk / blck'. + * However, in 16 bits - 8ch mode, this factor needs to be + * increased by 50% to get the correct output rate. + * No idea why ! + */ + if (params_width(params) == 16 && params_channels(params) == 8) { + if (bs % 2) { + dev_err(component->dev, + "Cannot increase i2s divider by 50%%\n"); + return -EINVAL; + } + bs += bs / 2; + } + + /* Use CLK_MORE for mclk to bclk divider */ + snd_soc_component_update_bits(component, AIU_CLK_CTRL, + AIU_CLK_CTRL_I2S_DIV, + FIELD_PREP(AIU_CLK_CTRL_I2S_DIV, 0)); + + snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, + AIU_CLK_CTRL_MORE_I2S_DIV, + FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV, + bs - 1)); + + return 0; +} + +static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct aiu *aiu = snd_soc_component_get_drvdata(component); + unsigned int srate = params_rate(params); + unsigned int fs, bs; + int ret; + + /* Get the oversampling factor */ + fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); + + if (fs % 64) + return -EINVAL; + + /* Send data MSB first */ + snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, + AIU_I2S_DAC_CFG_MSB_FIRST, + AIU_I2S_DAC_CFG_MSB_FIRST); + + /* Set bclk to lrlck ratio */ + snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, + AIU_CODEC_DAC_LRCLK_CTRL_DIV, + FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV, + 64 - 1)); + + bs = fs / 64; + + if (aiu->platform->has_clk_ctrl_more_i2s_div) + ret = aiu_encoder_i2s_set_more_div(component, params, bs); + else + ret = aiu_encoder_i2s_set_legacy_div(component, params, bs); + + if (ret) + return ret; + + /* Make sure amclk is used for HDMI i2s as well */ + snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, + AIU_CLK_CTRL_MORE_HDMI_AMCLK, + AIU_CLK_CTRL_MORE_HDMI_AMCLK); + + return 0; +} + +static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret; + + /* Disable the clock while changing the settings */ + aiu_encoder_i2s_divider_enable(component, false); + + ret = aiu_encoder_i2s_setup_desc(component, params); + if (ret) { + dev_err(dai->dev, "setting i2s desc failed\n"); + return ret; + } + + ret = aiu_encoder_i2s_set_clocks(component, params); + if (ret) { + dev_err(dai->dev, "setting i2s clocks failed\n"); + return ret; + } + + aiu_encoder_i2s_divider_enable(component, true); + + return 0; +} + +static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + aiu_encoder_i2s_divider_enable(component, false); + + return 0; +} + +static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; + unsigned int val = 0; + unsigned int skew; + + /* Only CPU Master / Codec Slave supported ATM */ + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) + return -EINVAL; + + if (inv == SND_SOC_DAIFMT_NB_IF || + inv == SND_SOC_DAIFMT_IB_IF) + val |= AIU_CLK_CTRL_LRCLK_INVERT; + + if (inv == SND_SOC_DAIFMT_IB_NF || + inv == SND_SOC_DAIFMT_IB_IF) + val |= AIU_CLK_CTRL_AOCLK_INVERT; + + /* Signal skew */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* Invert sample clock for i2s */ + val ^= AIU_CLK_CTRL_LRCLK_INVERT; + skew = 1; + break; + case SND_SOC_DAIFMT_LEFT_J: + skew = 0; + break; + default: + return -EINVAL; + } + + val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); + snd_soc_component_update_bits(component, AIU_CLK_CTRL, + AIU_CLK_CTRL_LRCLK_INVERT | + AIU_CLK_CTRL_AOCLK_INVERT | + AIU_CLK_CTRL_LRCLK_SKEW, + val); + + return 0; +} + +static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + int ret; + + if (WARN_ON(clk_id != 0)) + return -EINVAL; + + if (dir == SND_SOC_CLOCK_IN) + return 0; + + ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); + if (ret) + dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); + + return ret; +} + +static const unsigned int hw_channels[] = {2, 8}; +static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { + .list = hw_channels, + .count = ARRAY_SIZE(hw_channels), + .mask = 0, +}; + +static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + int ret; + + /* Make sure the encoder gets either 2 or 8 channels */ + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &hw_channel_constraints); + if (ret) { + dev_err(dai->dev, "adding channels constraints failed\n"); + return ret; + } + + ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); + if (ret) + dev_err(dai->dev, "failed to enable i2s clocks\n"); + + return ret; +} + +static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + + clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); +} + +const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { + .trigger = aiu_encoder_i2s_trigger, + .hw_params = aiu_encoder_i2s_hw_params, + .hw_free = aiu_encoder_i2s_hw_free, + .set_fmt = aiu_encoder_i2s_set_fmt, + .set_sysclk = aiu_encoder_i2s_set_sysclk, + .startup = aiu_encoder_i2s_startup, + .shutdown = aiu_encoder_i2s_shutdown, +}; + diff --git a/sound/soc/meson/aiu-encoder-spdif.c b/sound/soc/meson/aiu-encoder-spdif.c new file mode 100644 index 000000000000..de850913975f --- /dev/null +++ b/sound/soc/meson/aiu-encoder-spdif.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <sound/pcm_params.h> +#include <sound/pcm_iec958.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "aiu.h" + +#define AIU_958_MISC_NON_PCM BIT(0) +#define AIU_958_MISC_MODE_16BITS BIT(1) +#define AIU_958_MISC_16BITS_ALIGN GENMASK(6, 5) +#define AIU_958_MISC_MODE_32BITS BIT(7) +#define AIU_958_MISC_U_FROM_STREAM BIT(12) +#define AIU_958_MISC_FORCE_LR BIT(13) +#define AIU_958_CTRL_HOLD_EN BIT(0) +#define AIU_CLK_CTRL_958_DIV_EN BIT(1) +#define AIU_CLK_CTRL_958_DIV GENMASK(5, 4) +#define AIU_CLK_CTRL_958_DIV_MORE BIT(12) + +#define AIU_CS_WORD_LEN 4 +#define AIU_958_INTERNAL_DIV 2 + +static void +aiu_encoder_spdif_divider_enable(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_update_bits(component, AIU_CLK_CTRL, + AIU_CLK_CTRL_958_DIV_EN, + enable ? AIU_CLK_CTRL_958_DIV_EN : 0); +} + +static void aiu_encoder_spdif_hold(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_update_bits(component, AIU_958_CTRL, + AIU_958_CTRL_HOLD_EN, + enable ? AIU_958_CTRL_HOLD_EN : 0); +} + +static int +aiu_encoder_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + aiu_encoder_spdif_hold(component, false); + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + aiu_encoder_spdif_hold(component, true); + return 0; + + default: + return -EINVAL; + } +} + +static int aiu_encoder_spdif_setup_cs_word(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + u8 cs[AIU_CS_WORD_LEN]; + unsigned int val; + int ret; + + ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, + AIU_CS_WORD_LEN); + if (ret < 0) + return ret; + + /* Write the 1st half word */ + val = cs[1] | cs[0] << 8; + snd_soc_component_write(component, AIU_958_CHSTAT_L0, val); + snd_soc_component_write(component, AIU_958_CHSTAT_R0, val); + + /* Write the 2nd half word */ + val = cs[3] | cs[2] << 8; + snd_soc_component_write(component, AIU_958_CHSTAT_L1, val); + snd_soc_component_write(component, AIU_958_CHSTAT_R1, val); + + return 0; +} + +static int aiu_encoder_spdif_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 aiu *aiu = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mrate; + int ret; + + /* Disable the clock while changing the settings */ + aiu_encoder_spdif_divider_enable(component, false); + + switch (params_physical_width(params)) { + case 16: + val |= AIU_958_MISC_MODE_16BITS; + val |= FIELD_PREP(AIU_958_MISC_16BITS_ALIGN, 2); + break; + case 32: + val |= AIU_958_MISC_MODE_32BITS; + break; + default: + dev_err(dai->dev, "Unsupport physical width\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AIU_958_MISC, + AIU_958_MISC_NON_PCM | + AIU_958_MISC_MODE_16BITS | + AIU_958_MISC_16BITS_ALIGN | + AIU_958_MISC_MODE_32BITS | + AIU_958_MISC_FORCE_LR | + AIU_958_MISC_U_FROM_STREAM, + val); + + /* Set the stream channel status word */ + ret = aiu_encoder_spdif_setup_cs_word(component, params); + if (ret) { + dev_err(dai->dev, "failed to set channel status word\n"); + return ret; + } + + snd_soc_component_update_bits(component, AIU_CLK_CTRL, + AIU_CLK_CTRL_958_DIV | + AIU_CLK_CTRL_958_DIV_MORE, + FIELD_PREP(AIU_CLK_CTRL_958_DIV, + __ffs(AIU_958_INTERNAL_DIV))); + + /* 2 * 32bits per subframe * 2 channels = 128 */ + mrate = params_rate(params) * 128 * AIU_958_INTERNAL_DIV; + ret = clk_set_rate(aiu->spdif.clks[MCLK].clk, mrate); + if (ret) { + dev_err(dai->dev, "failed to set mclk rate\n"); + return ret; + } + + aiu_encoder_spdif_divider_enable(component, true); + + return 0; +} + +static int aiu_encoder_spdif_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + aiu_encoder_spdif_divider_enable(component, false); + + return 0; +} + +static int aiu_encoder_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + int ret; + + /* + * NOTE: Make sure the spdif block is on its own divider. + * + * The spdif can be clocked by the i2s master clock or its own + * clock. We should (in theory) change the source depending on the + * origin of the data. + * + * However, considering the clocking scheme used on these platforms, + * the master clocks will pick the same PLL source when they are + * playing from the same FIFO. The clock should be in sync so, it + * should not be necessary to reparent the spdif master clock. + */ + ret = clk_set_parent(aiu->spdif.clks[MCLK].clk, + aiu->spdif_mclk); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(aiu->spdif.clk_num, aiu->spdif.clks); + if (ret) + dev_err(dai->dev, "failed to enable spdif clocks\n"); + + return ret; +} + +static void aiu_encoder_spdif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + + clk_bulk_disable_unprepare(aiu->spdif.clk_num, aiu->spdif.clks); +} + +const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops = { + .trigger = aiu_encoder_spdif_trigger, + .hw_params = aiu_encoder_spdif_hw_params, + .hw_free = aiu_encoder_spdif_hw_free, + .startup = aiu_encoder_spdif_startup, + .shutdown = aiu_encoder_spdif_shutdown, +}; diff --git a/sound/soc/meson/aiu-fifo-i2s.c b/sound/soc/meson/aiu-fifo-i2s.c new file mode 100644 index 000000000000..9a5271ce80fe --- /dev/null +++ b/sound/soc/meson/aiu-fifo-i2s.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "aiu.h" +#include "aiu-fifo.h" + +#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) +#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) +#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) +#define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) +#define AIU_MEM_I2S_MASKS_IRQ_BLOCK GENMASK(31, 16) +#define AIU_MEM_I2S_CONTROL_MODE_16BIT BIT(6) +#define AIU_MEM_I2S_BUF_CNTL_INIT BIT(0) +#define AIU_RST_SOFT_I2S_FAST BIT(0) + +#define AIU_FIFO_I2S_BLOCK 256 + +static struct snd_pcm_hardware fifo_i2s_pcm = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = AIU_FORMATS, + .rate_min = 5512, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 8, + .period_bytes_min = AIU_FIFO_I2S_BLOCK, + .period_bytes_max = AIU_FIFO_I2S_BLOCK * USHRT_MAX, + .periods_min = 2, + .periods_max = UINT_MAX, + + /* No real justification for this */ + .buffer_bytes_max = 1 * 1024 * 1024, +}; + +static int aiu_fifo_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int val; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_soc_component_write(component, AIU_RST_SOFT, + AIU_RST_SOFT_I2S_FAST); + snd_soc_component_read(component, AIU_I2S_SYNC, &val); + break; + } + + return aiu_fifo_trigger(substream, cmd, dai); +} + +static int aiu_fifo_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret; + + ret = aiu_fifo_prepare(substream, dai); + if (ret) + return ret; + + snd_soc_component_update_bits(component, + AIU_MEM_I2S_BUF_CNTL, + AIU_MEM_I2S_BUF_CNTL_INIT, + AIU_MEM_I2S_BUF_CNTL_INIT); + snd_soc_component_update_bits(component, + AIU_MEM_I2S_BUF_CNTL, + AIU_MEM_I2S_BUF_CNTL_INIT, 0); + + return 0; +} + +static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aiu_fifo *fifo = dai->playback_dma_data; + unsigned int val; + int ret; + + ret = aiu_fifo_hw_params(substream, params, dai); + if (ret) + return ret; + + switch (params_physical_width(params)) { + case 16: + val = AIU_MEM_I2S_CONTROL_MODE_16BIT; + break; + case 32: + val = 0; + break; + default: + dev_err(dai->dev, "Unsupported physical width %u\n", + params_physical_width(params)); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL, + AIU_MEM_I2S_CONTROL_MODE_16BIT, + val); + + /* Setup the irq periodicity */ + val = params_period_bytes(params) / fifo->fifo_block; + val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val); + snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS, + AIU_MEM_I2S_MASKS_IRQ_BLOCK, val); + + return 0; +} + +const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = { + .trigger = aiu_fifo_i2s_trigger, + .prepare = aiu_fifo_i2s_prepare, + .hw_params = aiu_fifo_i2s_hw_params, + .hw_free = aiu_fifo_hw_free, + .startup = aiu_fifo_startup, + .shutdown = aiu_fifo_shutdown, +}; + +int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct aiu_fifo *fifo; + int ret; + + ret = aiu_fifo_dai_probe(dai); + if (ret) + return ret; + + fifo = dai->playback_dma_data; + + fifo->pcm = &fifo_i2s_pcm; + fifo->mem_offset = AIU_MEM_I2S_START; + fifo->fifo_block = AIU_FIFO_I2S_BLOCK; + fifo->pclk = aiu->i2s.clks[PCLK].clk; + fifo->irq = aiu->i2s.irq; + + return 0; +} diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c new file mode 100644 index 000000000000..44eb6faacf44 --- /dev/null +++ b/sound/soc/meson/aiu-fifo-spdif.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "aiu.h" +#include "aiu-fifo.h" + +#define AIU_IEC958_DCU_FF_CTRL_EN BIT(0) +#define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE BIT(1) +#define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE GENMASK(3, 2) +#define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD BIT(2) +#define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ BIT(3) +#define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4) +#define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK BIT(5) +#define AIU_IEC958_DCU_FF_CTRL_CONTINUE BIT(6) +#define AIU_MEM_IEC958_CONTROL_ENDIAN GENMASK(5, 3) +#define AIU_MEM_IEC958_CONTROL_RD_DDR BIT(6) +#define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7) +#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8) +#define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0) + +#define AIU_FIFO_SPDIF_BLOCK 8 + +static struct snd_pcm_hardware fifo_spdif_pcm = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = AIU_FORMATS, + .rate_min = 5512, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = AIU_FIFO_SPDIF_BLOCK, + .period_bytes_max = AIU_FIFO_SPDIF_BLOCK * USHRT_MAX, + .periods_min = 2, + .periods_max = UINT_MAX, + + /* No real justification for this */ + .buffer_bytes_max = 1 * 1024 * 1024, +}; + +static void fifo_spdif_dcu_enable(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, + AIU_IEC958_DCU_FF_CTRL_EN, + enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0); +} + +static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret; + + ret = aiu_fifo_trigger(substream, cmd, dai); + if (ret) + return ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + fifo_spdif_dcu_enable(component, true); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + fifo_spdif_dcu_enable(component, false); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fifo_spdif_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret; + + ret = aiu_fifo_prepare(substream, dai); + if (ret) + return ret; + + snd_soc_component_update_bits(component, + AIU_MEM_IEC958_BUF_CNTL, + AIU_MEM_IEC958_BUF_CNTL_INIT, + AIU_MEM_IEC958_BUF_CNTL_INIT); + snd_soc_component_update_bits(component, + AIU_MEM_IEC958_BUF_CNTL, + AIU_MEM_IEC958_BUF_CNTL_INIT, 0); + + return 0; +} + +static int fifo_spdif_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; + unsigned int val; + int ret; + + ret = aiu_fifo_hw_params(substream, params, dai); + if (ret) + return ret; + + val = AIU_MEM_IEC958_CONTROL_RD_DDR | + AIU_MEM_IEC958_CONTROL_MODE_LINEAR; + + switch (params_physical_width(params)) { + case 16: + val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT; + break; + case 32: + break; + default: + dev_err(dai->dev, "Unsupported physical width %u\n", + params_physical_width(params)); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL, + AIU_MEM_IEC958_CONTROL_ENDIAN | + AIU_MEM_IEC958_CONTROL_RD_DDR | + AIU_MEM_IEC958_CONTROL_MODE_LINEAR | + AIU_MEM_IEC958_CONTROL_MODE_16BIT, + val); + + /* Number bytes read by the FIFO between each IRQ */ + snd_soc_component_write(component, AIU_IEC958_BPF, + params_period_bytes(params)); + + /* + * AUTO_DISABLE and SYNC_HEAD are enabled by default but + * this should be disabled in PCM (uncompressed) mode + */ + snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, + AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE | + AIU_IEC958_DCU_FF_CTRL_IRQ_MODE | + AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN, + AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ); + + return 0; +} + +const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = { + .trigger = fifo_spdif_trigger, + .prepare = fifo_spdif_prepare, + .hw_params = fifo_spdif_hw_params, + .hw_free = aiu_fifo_hw_free, + .startup = aiu_fifo_startup, + .shutdown = aiu_fifo_shutdown, +}; + +int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct aiu_fifo *fifo; + int ret; + + ret = aiu_fifo_dai_probe(dai); + if (ret) + return ret; + + fifo = dai->playback_dma_data; + + fifo->pcm = &fifo_spdif_pcm; + fifo->mem_offset = AIU_MEM_IEC958_START; + fifo->fifo_block = 1; + fifo->pclk = aiu->spdif.clks[PCLK].clk; + fifo->irq = aiu->spdif.irq; + + return 0; +} diff --git a/sound/soc/meson/aiu-fifo.c b/sound/soc/meson/aiu-fifo.c new file mode 100644 index 000000000000..d9cede4c33ff --- /dev/null +++ b/sound/soc/meson/aiu-fifo.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "aiu-fifo.h" + +#define AIU_MEM_START 0x00 +#define AIU_MEM_RD 0x04 +#define AIU_MEM_END 0x08 +#define AIU_MEM_MASKS 0x0c +#define AIU_MEM_MASK_CH_RD GENMASK(7, 0) +#define AIU_MEM_MASK_CH_MEM GENMASK(15, 8) +#define AIU_MEM_CONTROL 0x10 +#define AIU_MEM_CONTROL_INIT BIT(0) +#define AIU_MEM_CONTROL_FILL_EN BIT(1) +#define AIU_MEM_CONTROL_EMPTY_EN BIT(2) + +static struct snd_soc_dai *aiu_fifo_dai(struct snd_pcm_substream *ss) +{ + struct snd_soc_pcm_runtime *rtd = ss->private_data; + + return asoc_rtd_to_cpu(rtd, 0); +} + +snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_dai *dai = aiu_fifo_dai(substream); + struct aiu_fifo *fifo = dai->playback_dma_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int addr; + + snd_soc_component_read(component, fifo->mem_offset + AIU_MEM_RD, + &addr); + + return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr); +} + +static void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable) +{ + struct snd_soc_component *component = dai->component; + struct aiu_fifo *fifo = dai->playback_dma_data; + unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN | + AIU_MEM_CONTROL_EMPTY_EN); + + snd_soc_component_update_bits(component, + fifo->mem_offset + AIU_MEM_CONTROL, + en_mask, enable ? en_mask : 0); +} + +int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + aiu_fifo_enable(dai, true); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + aiu_fifo_enable(dai, false); + break; + default: + return -EINVAL; + } + + return 0; +} + +int aiu_fifo_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aiu_fifo *fifo = dai->playback_dma_data; + + snd_soc_component_update_bits(component, + fifo->mem_offset + AIU_MEM_CONTROL, + AIU_MEM_CONTROL_INIT, + AIU_MEM_CONTROL_INIT); + snd_soc_component_update_bits(component, + fifo->mem_offset + AIU_MEM_CONTROL, + AIU_MEM_CONTROL_INIT, 0); + return 0; +} + +int aiu_fifo_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_component *component = dai->component; + struct aiu_fifo *fifo = dai->playback_dma_data; + dma_addr_t end; + int ret; + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) + return ret; + + /* Setup the fifo boundaries */ + end = runtime->dma_addr + runtime->dma_bytes - fifo->fifo_block; + snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_START, + runtime->dma_addr); + snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_RD, + runtime->dma_addr); + snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_END, + end); + + /* Setup the fifo to read all the memory - no skip */ + snd_soc_component_update_bits(component, + fifo->mem_offset + AIU_MEM_MASKS, + AIU_MEM_MASK_CH_RD | AIU_MEM_MASK_CH_MEM, + FIELD_PREP(AIU_MEM_MASK_CH_RD, 0xff) | + FIELD_PREP(AIU_MEM_MASK_CH_MEM, 0xff)); + + return 0; +} + +int aiu_fifo_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_lib_free_pages(substream); +} + +static irqreturn_t aiu_fifo_isr(int irq, void *dev_id) +{ + struct snd_pcm_substream *playback = dev_id; + + snd_pcm_period_elapsed(playback); + + return IRQ_HANDLED; +} + +int aiu_fifo_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct aiu_fifo *fifo = dai->playback_dma_data; + int ret; + + snd_soc_set_runtime_hwparams(substream, fifo->pcm); + + /* + * Make sure the buffer and period size are multiple of the fifo burst + * size + */ + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + fifo->fifo_block); + if (ret) + return ret; + + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + fifo->fifo_block); + if (ret) + return ret; + + ret = clk_prepare_enable(fifo->pclk); + if (ret) + return ret; + + ret = request_irq(fifo->irq, aiu_fifo_isr, 0, dev_name(dai->dev), + substream); + if (ret) + clk_disable_unprepare(fifo->pclk); + + return ret; +} + +void aiu_fifo_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct aiu_fifo *fifo = dai->playback_dma_data; + + free_irq(fifo->irq, substream); + clk_disable_unprepare(fifo->pclk); +} + +int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) +{ + struct snd_pcm_substream *substream = + rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_card *card = rtd->card->snd_card; + struct aiu_fifo *fifo = dai->playback_dma_data; + size_t size = fifo->pcm->buffer_bytes_max; + + snd_pcm_lib_preallocate_pages(substream, + SNDRV_DMA_TYPE_DEV, + card->dev, size, size); + + return 0; +} + +int aiu_fifo_dai_probe(struct snd_soc_dai *dai) +{ + struct aiu_fifo *fifo; + + fifo = kzalloc(sizeof(*fifo), GFP_KERNEL); + if (!fifo) + return -ENOMEM; + + dai->playback_dma_data = fifo; + + return 0; +} + +int aiu_fifo_dai_remove(struct snd_soc_dai *dai) +{ + kfree(dai->playback_dma_data); + + return 0; +} + diff --git a/sound/soc/meson/aiu-fifo.h b/sound/soc/meson/aiu-fifo.h new file mode 100644 index 000000000000..42ce266677cc --- /dev/null +++ b/sound/soc/meson/aiu-fifo.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2020 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_AIU_FIFO_H +#define _MESON_AIU_FIFO_H + +struct snd_pcm_hardware; +struct snd_soc_component_driver; +struct snd_soc_dai_driver; +struct clk; +struct snd_pcm_ops; +struct snd_pcm_substream; +struct snd_soc_dai; +struct snd_pcm_hw_params; +struct platform_device; + +struct aiu_fifo { + struct snd_pcm_hardware *pcm; + unsigned int mem_offset; + unsigned int fifo_block; + struct clk *pclk; + int irq; +}; + +int aiu_fifo_dai_probe(struct snd_soc_dai *dai); +int aiu_fifo_dai_remove(struct snd_soc_dai *dai); + +snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + +int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai); +int aiu_fifo_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int aiu_fifo_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +int aiu_fifo_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int aiu_fifo_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +void aiu_fifo_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai); + +#endif /* _MESON_AIU_FIFO_H */ diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c new file mode 100644 index 000000000000..dc35ca79021c --- /dev/null +++ b/sound/soc/meson/aiu.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include <dt-bindings/sound/meson-aiu.h> +#include "aiu.h" +#include "aiu-fifo.h" + +#define AIU_I2S_MISC_958_SRC_SHIFT 3 + +static const char * const aiu_spdif_encode_sel_texts[] = { + "SPDIF", "I2S", +}; + +static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, + AIU_I2S_MISC_958_SRC_SHIFT, + aiu_spdif_encode_sel_texts); + +static const struct snd_kcontrol_new aiu_spdif_encode_mux = + SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); + +static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { + SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, + &aiu_spdif_encode_mux), +}; + +static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { + { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, + { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, + { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, + { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, +}; + +int aiu_of_xlate_dai_name(struct snd_soc_component *component, + struct of_phandle_args *args, + const char **dai_name, + unsigned int component_id) +{ + struct snd_soc_dai *dai; + int id; + + if (args->args_count != 2) + return -EINVAL; + + if (args->args[0] != component_id) + return -EINVAL; + + id = args->args[1]; + + if (id < 0 || id >= component->num_dai) + return -EINVAL; + + for_each_component_dais(component, dai) { + if (id == 0) + break; + id--; + } + + *dai_name = dai->driver->name; + + return 0; +} + +static int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component, + struct of_phandle_args *args, + const char **dai_name) +{ + return aiu_of_xlate_dai_name(component, args, dai_name, AIU_CPU); +} + +static int aiu_cpu_component_probe(struct snd_soc_component *component) +{ + struct aiu *aiu = snd_soc_component_get_drvdata(component); + + /* Required for the SPDIF Source control operation */ + return clk_prepare_enable(aiu->i2s.clks[PCLK].clk); +} + +static void aiu_cpu_component_remove(struct snd_soc_component *component) +{ + struct aiu *aiu = snd_soc_component_get_drvdata(component); + + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); +} + +static const struct snd_soc_component_driver aiu_cpu_component = { + .name = "AIU CPU", + .dapm_widgets = aiu_cpu_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aiu_cpu_dapm_widgets), + .dapm_routes = aiu_cpu_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aiu_cpu_dapm_routes), + .of_xlate_dai_name = aiu_cpu_of_xlate_dai_name, + .pointer = aiu_fifo_pointer, + .probe = aiu_cpu_component_probe, + .remove = aiu_cpu_component_remove, +}; + +static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { + [CPU_I2S_FIFO] = { + .name = "I2S FIFO", + .playback = { + .stream_name = "I2S FIFO Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5512, + .rate_max = 192000, + .formats = AIU_FORMATS, + }, + .ops = &aiu_fifo_i2s_dai_ops, + .pcm_new = aiu_fifo_pcm_new, + .probe = aiu_fifo_i2s_dai_probe, + .remove = aiu_fifo_dai_remove, + }, + [CPU_SPDIF_FIFO] = { + .name = "SPDIF FIFO", + .playback = { + .stream_name = "SPDIF FIFO Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5512, + .rate_max = 192000, + .formats = AIU_FORMATS, + }, + .ops = &aiu_fifo_spdif_dai_ops, + .pcm_new = aiu_fifo_pcm_new, + .probe = aiu_fifo_spdif_dai_probe, + .remove = aiu_fifo_dai_remove, + }, + [CPU_I2S_ENCODER] = { + .name = "I2S Encoder", + .playback = { + .stream_name = "I2S Encoder Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = AIU_FORMATS, + }, + .ops = &aiu_encoder_i2s_dai_ops, + }, + [CPU_SPDIF_ENCODER] = { + .name = "SPDIF Encoder", + .playback = { + .stream_name = "SPDIF Encoder Playback", + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000), + .formats = AIU_FORMATS, + }, + .ops = &aiu_encoder_spdif_dai_ops, + } +}; + +static const struct regmap_config aiu_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x2ac, +}; + +static int aiu_clk_bulk_get(struct device *dev, + const char * const *ids, + unsigned int num, + struct aiu_interface *interface) +{ + struct clk_bulk_data *clks; + int i, ret; + + clks = devm_kcalloc(dev, num, sizeof(*clks), GFP_KERNEL); + if (!clks) + return -ENOMEM; + + for (i = 0; i < num; i++) + clks[i].id = ids[i]; + + ret = devm_clk_bulk_get(dev, num, clks); + if (ret < 0) + return ret; + + interface->clks = clks; + interface->clk_num = num; + return 0; +} + +static const char * const aiu_i2s_ids[] = { + [PCLK] = "i2s_pclk", + [AOCLK] = "i2s_aoclk", + [MCLK] = "i2s_mclk", + [MIXER] = "i2s_mixer", +}; + +static const char * const aiu_spdif_ids[] = { + [PCLK] = "spdif_pclk", + [AOCLK] = "spdif_aoclk", + [MCLK] = "spdif_mclk_sel" +}; + +static int aiu_clk_get(struct device *dev) +{ + struct aiu *aiu = dev_get_drvdata(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); + } + + 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); + } + + 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; + } + + 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; + } + + ret = clk_prepare_enable(aiu->pclk); + if (ret) { + dev_err(dev, "peripheral clock enable failed\n"); + return ret; + } + + ret = devm_add_action_or_reset(dev, + (void(*)(void *))clk_disable_unprepare, + aiu->pclk); + if (ret) + dev_err(dev, "failed to add reset action on pclk"); + + return ret; +} + +static int aiu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + void __iomem *regs; + struct regmap *map; + struct aiu *aiu; + int ret; + + aiu = devm_kzalloc(dev, sizeof(*aiu), GFP_KERNEL); + if (!aiu) + return -ENOMEM; + + aiu->platform = device_get_match_data(dev); + if (!aiu->platform) + return -ENODEV; + + 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; + } + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + map = devm_regmap_init_mmio(dev, regs, &aiu_regmap_cfg); + if (IS_ERR(map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(map)); + return PTR_ERR(map); + } + + aiu->i2s.irq = platform_get_irq_byname(pdev, "i2s"); + if (aiu->i2s.irq < 0) + return aiu->i2s.irq; + + aiu->spdif.irq = platform_get_irq_byname(pdev, "spdif"); + if (aiu->spdif.irq < 0) + return aiu->spdif.irq; + + ret = aiu_clk_get(dev); + if (ret) + return ret; + + /* Register the cpu component of the aiu */ + ret = snd_soc_register_component(dev, &aiu_cpu_component, + aiu_cpu_dai_drv, + ARRAY_SIZE(aiu_cpu_dai_drv)); + if (ret) { + dev_err(dev, "Failed to register cpu component\n"); + return ret; + } + + /* Register the hdmi codec control component */ + ret = aiu_hdmi_ctrl_register_component(dev); + if (ret) { + dev_err(dev, "Failed to register hdmi control component\n"); + goto err; + } + + /* Register the internal dac control component on gxl */ + if (aiu->platform->has_acodec) { + ret = aiu_acodec_ctrl_register_component(dev); + if (ret) { + dev_err(dev, + "Failed to register acodec control component\n"); + goto err; + } + } + + return 0; +err: + snd_soc_unregister_component(dev); + return ret; +} + +static int aiu_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + + return 0; +} + +static const struct aiu_platform_data aiu_gxbb_pdata = { + .has_acodec = false, + .has_clk_ctrl_more_i2s_div = true, +}; + +static const struct aiu_platform_data aiu_gxl_pdata = { + .has_acodec = true, + .has_clk_ctrl_more_i2s_div = true, +}; + +static const struct aiu_platform_data aiu_meson8_pdata = { + .has_acodec = false, + .has_clk_ctrl_more_i2s_div = false, +}; + +static const struct of_device_id aiu_of_match[] = { + { .compatible = "amlogic,aiu-gxbb", .data = &aiu_gxbb_pdata }, + { .compatible = "amlogic,aiu-gxl", .data = &aiu_gxl_pdata }, + { .compatible = "amlogic,aiu-meson8", .data = &aiu_meson8_pdata }, + { .compatible = "amlogic,aiu-meson8b", .data = &aiu_meson8_pdata }, + {} +}; +MODULE_DEVICE_TABLE(of, aiu_of_match); + +static struct platform_driver aiu_pdrv = { + .probe = aiu_probe, + .remove = aiu_remove, + .driver = { + .name = "meson-aiu", + .of_match_table = aiu_of_match, + }, +}; +module_platform_driver(aiu_pdrv); + +MODULE_DESCRIPTION("Meson AIU Driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h new file mode 100644 index 000000000000..87aa19ac4af3 --- /dev/null +++ b/sound/soc/meson/aiu.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_AIU_H +#define _MESON_AIU_H + +struct clk; +struct clk_bulk_data; +struct device; +struct of_phandle_args; +struct snd_soc_dai; +struct snd_soc_dai_ops; + +enum aiu_clk_ids { + PCLK = 0, + AOCLK, + MCLK, + MIXER +}; + +struct aiu_interface { + struct clk_bulk_data *clks; + unsigned int clk_num; + int irq; +}; + +struct aiu_platform_data { + bool has_acodec; + bool has_clk_ctrl_more_i2s_div; +}; + +struct aiu { + struct clk *pclk; + struct clk *spdif_mclk; + struct aiu_interface i2s; + struct aiu_interface spdif; + const struct aiu_platform_data *platform; +}; + +#define AIU_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +int aiu_of_xlate_dai_name(struct snd_soc_component *component, + struct of_phandle_args *args, + const char **dai_name, + unsigned int component_id); + +int aiu_hdmi_ctrl_register_component(struct device *dev); +int aiu_acodec_ctrl_register_component(struct device *dev); + +int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai); +int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai); + +extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops; +extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops; +extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops; +extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops; + +#define AIU_IEC958_BPF 0x000 +#define AIU_958_MISC 0x010 +#define AIU_IEC958_DCU_FF_CTRL 0x01c +#define AIU_958_CHSTAT_L0 0x020 +#define AIU_958_CHSTAT_L1 0x024 +#define AIU_958_CTRL 0x028 +#define AIU_I2S_SOURCE_DESC 0x034 +#define AIU_I2S_DAC_CFG 0x040 +#define AIU_I2S_SYNC 0x044 +#define AIU_I2S_MISC 0x048 +#define AIU_RST_SOFT 0x054 +#define AIU_CLK_CTRL 0x058 +#define AIU_CLK_CTRL_MORE 0x064 +#define AIU_CODEC_DAC_LRCLK_CTRL 0x0a0 +#define AIU_HDMI_CLK_DATA_CTRL 0x0a8 +#define AIU_ACODEC_CTRL 0x0b0 +#define AIU_958_CHSTAT_R0 0x0c0 +#define AIU_958_CHSTAT_R1 0x0c4 +#define AIU_MEM_I2S_START 0x180 +#define AIU_MEM_I2S_MASKS 0x18c +#define AIU_MEM_I2S_CONTROL 0x190 +#define AIU_MEM_IEC958_START 0x194 +#define AIU_MEM_IEC958_CONTROL 0x1a4 +#define AIU_MEM_I2S_BUF_CNTL 0x1d8 +#define AIU_MEM_IEC958_BUF_CNTL 0x1fc + +#endif /* _MESON_AIU_H */ diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index 1f698adde506..af46845f4ef2 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -9,11 +9,7 @@ #include <sound/soc-dai.h> #include "axg-tdm.h" - -struct axg_card { - struct snd_soc_card card; - void **link_data; -}; +#include "meson-card.h" struct axg_dai_link_tdm_mask { u32 tx; @@ -41,161 +37,15 @@ static const struct snd_soc_pcm_stream codec_params = { .channels_max = 8, }; -#define PREFIX "amlogic," - -static int axg_card_reallocate_links(struct axg_card *priv, - unsigned int num_links) -{ - struct snd_soc_dai_link *links; - void **ldata; - - links = krealloc(priv->card.dai_link, - num_links * sizeof(*priv->card.dai_link), - GFP_KERNEL | __GFP_ZERO); - ldata = krealloc(priv->link_data, - num_links * sizeof(*priv->link_data), - GFP_KERNEL | __GFP_ZERO); - - if (!links || !ldata) { - dev_err(priv->card.dev, "failed to allocate links\n"); - return -ENOMEM; - } - - priv->card.dai_link = links; - priv->link_data = ldata; - priv->card.num_links = num_links; - return 0; -} - -static int axg_card_parse_dai(struct snd_soc_card *card, - struct device_node *node, - struct device_node **dai_of_node, - const char **dai_name) -{ - struct of_phandle_args args; - int ret; - - if (!dai_name || !dai_of_node || !node) - return -EINVAL; - - 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; - } - *dai_of_node = args.np; - - return snd_soc_get_dai_name(&args, dai_name); -} - -static int axg_card_set_link_name(struct snd_soc_card *card, - struct snd_soc_dai_link *link, - struct device_node *node, - const char *prefix) -{ - char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", - prefix, node->full_name); - if (!name) - return -ENOMEM; - - link->name = name; - link->stream_name = name; - - return 0; -} - -static void axg_card_clean_references(struct axg_card *priv) -{ - struct snd_soc_card *card = &priv->card; - struct snd_soc_dai_link *link; - struct snd_soc_dai_link_component *codec; - struct snd_soc_aux_dev *aux; - int i, j; - - if (card->dai_link) { - for_each_card_prelinks(card, i, link) { - if (link->cpus) - of_node_put(link->cpus->of_node); - for_each_link_codecs(link, j, codec) - of_node_put(codec->of_node); - } - } - - if (card->aux_dev) { - for_each_card_pre_auxs(card, i, aux) - of_node_put(aux->dlc.of_node); - } - - kfree(card->dai_link); - kfree(priv->link_data); -} - -static int axg_card_add_aux_devices(struct snd_soc_card *card) -{ - struct device_node *node = card->dev->of_node; - struct snd_soc_aux_dev *aux; - int num, i; - - num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); - if (num == -ENOENT) { - /* - * It is ok to have no auxiliary devices but for this card it - * is a strange situtation. Let's warn the about it. - */ - dev_warn(card->dev, "card has no auxiliary devices\n"); - return 0; - } else if (num < 0) { - dev_err(card->dev, "error getting auxiliary devices: %d\n", - num); - return num; - } - - aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); - if (!aux) - return -ENOMEM; - card->aux_dev = aux; - card->num_aux_devs = num; - - for_each_card_pre_auxs(card, i, aux) { - aux->dlc.of_node = - of_parse_phandle(node, "audio-aux-devs", i); - if (!aux->dlc.of_node) - return -EINVAL; - } - - return 0; -} - static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); struct axg_dai_link_tdm_data *be = (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; - struct snd_soc_dai *codec_dai; - unsigned int mclk; - int ret, i; - - if (be->mclk_fs) { - mclk = params_rate(params) * be->mclk_fs; - - for_each_rtd_codec_dai(rtd, i, codec_dai) { - ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); - if (ret && ret != -ENOTSUPP) - return ret; - } - - ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, - SND_SOC_CLOCK_OUT); - if (ret && ret != -ENOTSUPP) - return ret; - } - return 0; + return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs); } static const struct snd_soc_ops axg_card_tdm_be_ops = { @@ -204,13 +54,13 @@ static const struct snd_soc_ops axg_card_tdm_be_ops = { static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); struct axg_dai_link_tdm_data *be = (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; struct snd_soc_dai *codec_dai; int ret, i; - for_each_rtd_codec_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { ret = snd_soc_dai_set_tdm_slot(codec_dai, be->codec_masks[i].tx, be->codec_masks[i].rx, @@ -222,10 +72,10 @@ static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) } } - ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, be->tx_mask, be->rx_mask, + ret = axg_tdm_set_tdm_slots(asoc_rtd_to_cpu(rtd, 0), be->tx_mask, be->rx_mask, be->slots, be->slot_width); if (ret) { - dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n"); + dev_err(asoc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n"); return ret; } @@ -234,16 +84,16 @@ static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) { - struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); struct axg_dai_link_tdm_data *be = (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; int ret; /* The loopback rx_mask is the pad tx_mask */ - ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, NULL, be->tx_mask, + ret = axg_tdm_set_tdm_slots(asoc_rtd_to_cpu(rtd, 0), NULL, be->tx_mask, be->slots, be->slot_width); if (ret) { - dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n"); + dev_err(asoc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n"); return ret; } @@ -253,14 +103,14 @@ static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) static int axg_card_add_tdm_loopback(struct snd_soc_card *card, int *index) { - struct axg_card *priv = snd_soc_card_get_drvdata(card); + struct meson_card *priv = snd_soc_card_get_drvdata(card); struct snd_soc_dai_link *pad = &card->dai_link[*index]; struct snd_soc_dai_link *lb; struct snd_soc_dai_link_component *dlc; int ret; /* extend links */ - ret = axg_card_reallocate_links(priv, card->num_links + 1); + ret = meson_card_reallocate_links(card, card->num_links + 1); if (ret) return ret; @@ -304,32 +154,6 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, return 0; } -static unsigned int axg_card_parse_daifmt(struct device_node *node, - struct device_node *cpu_node) -{ - struct device_node *bitclkmaster = NULL; - struct device_node *framemaster = NULL; - unsigned int daifmt; - - daifmt = snd_soc_of_parse_daifmt(node, PREFIX, - &bitclkmaster, &framemaster); - daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - - /* If no master is provided, default to cpu master */ - if (!bitclkmaster || bitclkmaster == cpu_node) { - daifmt |= (!framemaster || framemaster == cpu_node) ? - SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; - } else { - daifmt |= (!framemaster || framemaster == cpu_node) ? - SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; - } - - of_node_put(bitclkmaster); - of_node_put(framemaster); - - return daifmt; -} - static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, struct snd_soc_dai_link *link, struct device_node *node, @@ -424,7 +248,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, struct device_node *node, int *index) { - struct axg_card *priv = snd_soc_card_get_drvdata(card); + struct meson_card *priv = snd_soc_card_get_drvdata(card); struct snd_soc_dai_link *link = &card->dai_link[*index]; struct axg_dai_link_tdm_data *be; int ret; @@ -438,7 +262,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, /* Setup tdm link */ link->ops = &axg_card_tdm_be_ops; link->init = axg_card_tdm_dai_init; - link->dai_fmt = axg_card_parse_daifmt(node, link->cpus->of_node); + link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node); of_property_read_u32(node, "mclk-fs", &be->mclk_fs); @@ -462,97 +286,25 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, return 0; } -static int axg_card_set_be_link(struct snd_soc_card *card, - struct snd_soc_dai_link *link, - struct device_node *node) -{ - struct snd_soc_dai_link_component *codec; - struct device_node *np; - int ret, num_codecs; - - link->no_pcm = 1; - link->dpcm_playback = 1; - link->dpcm_capture = 1; - - num_codecs = of_get_child_count(node); - if (!num_codecs) { - dev_err(card->dev, "be link %s has no codec\n", - node->full_name); - return -EINVAL; - } - - codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); - if (!codec) - return -ENOMEM; - - link->codecs = codec; - link->num_codecs = num_codecs; - - for_each_child_of_node(node, np) { - ret = axg_card_parse_dai(card, np, &codec->of_node, - &codec->dai_name); - if (ret) { - of_node_put(np); - return ret; - } - - codec++; - } - - ret = axg_card_set_link_name(card, link, node, "be"); - if (ret) - dev_err(card->dev, "error setting %pOFn link name\n", np); - - return ret; -} - -static int axg_card_set_fe_link(struct snd_soc_card *card, - struct snd_soc_dai_link *link, - struct device_node *node, - bool is_playback) -{ - struct snd_soc_dai_link_component *codec; - - codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL); - if (!codec) - return -ENOMEM; - - link->codecs = codec; - link->num_codecs = 1; - - link->dynamic = 1; - link->dpcm_merged_format = 1; - link->dpcm_merged_chan = 1; - link->dpcm_merged_rate = 1; - link->codecs->dai_name = "snd-soc-dummy-dai"; - link->codecs->name = "snd-soc-dummy"; - - if (is_playback) - link->dpcm_playback = 1; - else - link->dpcm_capture = 1; - - return axg_card_set_link_name(card, link, node, "fe"); -} - static int axg_card_cpu_is_capture_fe(struct device_node *np) { - return of_device_is_compatible(np, PREFIX "axg-toddr"); + return of_device_is_compatible(np, DT_PREFIX "axg-toddr"); } static int axg_card_cpu_is_playback_fe(struct device_node *np) { - return of_device_is_compatible(np, PREFIX "axg-frddr"); + return of_device_is_compatible(np, DT_PREFIX "axg-frddr"); } static int axg_card_cpu_is_tdm_iface(struct device_node *np) { - return of_device_is_compatible(np, PREFIX "axg-tdm-iface"); + return of_device_is_compatible(np, DT_PREFIX "axg-tdm-iface"); } static int axg_card_cpu_is_codec(struct device_node *np) { - return of_device_is_compatible(np, PREFIX "g12a-tohdmitx"); + return of_device_is_compatible(np, DT_PREFIX "g12a-tohdmitx") || + of_device_is_compatible(np, DT_PREFIX "g12a-toacodec"); } static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, @@ -569,17 +321,17 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, dai_link->cpus = cpu; dai_link->num_cpus = 1; - ret = axg_card_parse_dai(card, np, &dai_link->cpus->of_node, - &dai_link->cpus->dai_name); + ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node, + &dai_link->cpus->dai_name); if (ret) return ret; if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node)) - ret = axg_card_set_fe_link(card, dai_link, np, true); + ret = meson_card_set_fe_link(card, dai_link, np, true); else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node)) - ret = axg_card_set_fe_link(card, dai_link, np, false); + ret = meson_card_set_fe_link(card, dai_link, np, false); else - ret = axg_card_set_be_link(card, dai_link, np); + ret = meson_card_set_be_link(card, dai_link, np); if (ret) return ret; @@ -592,121 +344,21 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, return ret; } -static int axg_card_add_links(struct snd_soc_card *card) -{ - struct axg_card *priv = snd_soc_card_get_drvdata(card); - struct device_node *node = card->dev->of_node; - struct device_node *np; - int num, i, ret; - - num = of_get_child_count(node); - if (!num) { - dev_err(card->dev, "card has no links\n"); - return -EINVAL; - } - - ret = axg_card_reallocate_links(priv, num); - if (ret) - return ret; - - i = 0; - for_each_child_of_node(node, np) { - ret = axg_card_add_link(card, np, &i); - if (ret) { - of_node_put(np); - return ret; - } - - i++; - } - - return 0; -} - -static int axg_card_parse_of_optional(struct snd_soc_card *card, - const char *propname, - int (*func)(struct snd_soc_card *c, - const char *p)) -{ - /* If property is not provided, don't fail ... */ - if (!of_property_read_bool(card->dev->of_node, propname)) - return 0; - - /* ... but do fail if it is provided and the parsing fails */ - return func(card, propname); -} +static const struct meson_card_match_data axg_card_match_data = { + .add_link = axg_card_add_link, +}; static const struct of_device_id axg_card_of_match[] = { - { .compatible = "amlogic,axg-sound-card", }, - {} + { + .compatible = "amlogic,axg-sound-card", + .data = &axg_card_match_data, + }, {} }; MODULE_DEVICE_TABLE(of, axg_card_of_match); -static int axg_card_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct axg_card *priv; - int ret; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - platform_set_drvdata(pdev, priv); - snd_soc_card_set_drvdata(&priv->card, priv); - - priv->card.owner = THIS_MODULE; - priv->card.dev = dev; - - ret = snd_soc_of_parse_card_name(&priv->card, "model"); - if (ret < 0) - return ret; - - ret = axg_card_parse_of_optional(&priv->card, "audio-routing", - snd_soc_of_parse_audio_routing); - if (ret) { - dev_err(dev, "error while parsing routing\n"); - return ret; - } - - ret = axg_card_parse_of_optional(&priv->card, "audio-widgets", - snd_soc_of_parse_audio_simple_widgets); - if (ret) { - dev_err(dev, "error while parsing widgets\n"); - return ret; - } - - ret = axg_card_add_links(&priv->card); - if (ret) - goto out_err; - - ret = axg_card_add_aux_devices(&priv->card); - if (ret) - goto out_err; - - ret = devm_snd_soc_register_card(dev, &priv->card); - if (ret) - goto out_err; - - return 0; - -out_err: - axg_card_clean_references(priv); - return ret; -} - -static int axg_card_remove(struct platform_device *pdev) -{ - struct axg_card *priv = platform_get_drvdata(pdev); - - axg_card_clean_references(priv); - - return 0; -} - static struct platform_driver axg_card_pdrv = { - .probe = axg_card_probe, - .remove = axg_card_remove, + .probe = meson_card_probe, + .remove = meson_card_remove, .driver = { .name = "axg-sound-card", .of_match_table = axg_card_of_match, diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index c12b0d5e8ebf..2e9b56b29d31 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -47,7 +47,7 @@ static struct snd_soc_dai *axg_fifo_dai(struct snd_pcm_substream *ss) { struct snd_soc_pcm_runtime *rtd = ss->private_data; - return rtd->cpu_dai; + return asoc_rtd_to_cpu(rtd, 0); } static struct axg_fifo *axg_fifo_data(struct snd_pcm_substream *ss) diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c new file mode 100644 index 000000000000..9339fabccb79 --- /dev/null +++ b/sound/soc/meson/g12a-toacodec.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <sound/pcm_params.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include <dt-bindings/sound/meson-g12a-toacodec.h> +#include "axg-tdm.h" +#include "meson-codec-glue.h" + +#define G12A_TOACODEC_DRV_NAME "g12a-toacodec" + +#define TOACODEC_CTRL0 0x0 +#define CTRL0_ENABLE_SHIFT 31 +#define CTRL0_DAT_SEL_SHIFT 14 +#define CTRL0_DAT_SEL (0x3 << CTRL0_DAT_SEL_SHIFT) +#define CTRL0_LANE_SEL 12 +#define CTRL0_LRCLK_SEL GENMASK(9, 8) +#define CTRL0_BLK_CAP_INV BIT(7) +#define CTRL0_BCLK_O_INV BIT(6) +#define CTRL0_BCLK_SEL GENMASK(5, 4) +#define CTRL0_MCLK_SEL GENMASK(2, 0) + +#define TOACODEC_OUT_CHMAX 2 + +static const char * const g12a_toacodec_mux_texts[] = { + "I2S A", "I2S B", "I2S C", +}; + +static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, changed; + + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); + changed = snd_soc_component_test_bits(component, e->reg, + CTRL0_DAT_SEL, + FIELD_PREP(CTRL0_DAT_SEL, mux)); + + if (!changed) + return 0; + + /* Force disconnect of the mux while updating */ + snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); + + snd_soc_component_update_bits(component, e->reg, + CTRL0_DAT_SEL | + CTRL0_LRCLK_SEL | + CTRL0_BCLK_SEL, + FIELD_PREP(CTRL0_DAT_SEL, mux) | + FIELD_PREP(CTRL0_LRCLK_SEL, mux) | + FIELD_PREP(CTRL0_BCLK_SEL, mux)); + + /* + * FIXME: + * On this soc, the glue gets the MCLK directly from the clock + * controller instead of going the through the TDM interface. + * + * Here we assume interface A uses clock A, etc ... While it is + * true for now, it could be different. Instead the glue should + * find out the clock used by the interface and select the same + * source. For that, we will need regmap backed clock mux which + * is a work in progress + */ + snd_soc_component_update_bits(component, e->reg, + CTRL0_MCLK_SEL, + FIELD_PREP(CTRL0_MCLK_SEL, mux)); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); + + return 0; +} + +static SOC_ENUM_SINGLE_DECL(g12a_toacodec_mux_enum, TOACODEC_CTRL0, + CTRL0_DAT_SEL_SHIFT, + g12a_toacodec_mux_texts); + +static const struct snd_kcontrol_new g12a_toacodec_mux = + SOC_DAPM_ENUM_EXT("Source", g12a_toacodec_mux_enum, + snd_soc_dapm_get_enum_double, + g12a_toacodec_mux_put_enum); + +static const struct snd_kcontrol_new g12a_toacodec_out_enable = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOACODEC_CTRL0, + CTRL0_ENABLE_SHIFT, 1, 0); + +static const struct snd_soc_dapm_widget g12a_toacodec_widgets[] = { + SND_SOC_DAPM_MUX("SRC", SND_SOC_NOPM, 0, 0, + &g12a_toacodec_mux), + SND_SOC_DAPM_SWITCH("OUT EN", SND_SOC_NOPM, 0, 0, + &g12a_toacodec_out_enable), +}; + +static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct meson_codec_glue_input *data; + int ret; + + ret = meson_codec_glue_input_hw_params(substream, params, dai); + if (ret) + return ret; + + /* The glue will provide 1 lane out of the 4 to the output */ + data = meson_codec_glue_input_get_data(dai); + data->params.channels_min = min_t(unsigned int, TOACODEC_OUT_CHMAX, + data->params.channels_min); + data->params.channels_max = min_t(unsigned int, TOACODEC_OUT_CHMAX, + data->params.channels_max); + + return 0; +} + +static const struct snd_soc_dai_ops g12a_toacodec_input_ops = { + .hw_params = g12a_toacodec_input_hw_params, + .set_fmt = meson_codec_glue_input_set_fmt, +}; + +static const struct snd_soc_dai_ops g12a_toacodec_output_ops = { + .startup = meson_codec_glue_output_startup, +}; + +#define TOACODEC_STREAM(xname, xsuffix, xchmax) \ +{ \ + .stream_name = xname " " xsuffix, \ + .channels_min = 1, \ + .channels_max = (xchmax), \ + .rate_min = 5512, \ + .rate_max = 192000, \ + .formats = AXG_TDM_FORMATS, \ +} + +#define TOACODEC_INPUT(xname, xid) { \ + .name = xname, \ + .id = (xid), \ + .playback = TOACODEC_STREAM(xname, "Playback", 8), \ + .ops = &g12a_toacodec_input_ops, \ + .probe = meson_codec_glue_input_dai_probe, \ + .remove = meson_codec_glue_input_dai_remove, \ +} + +#define TOACODEC_OUTPUT(xname, xid) { \ + .name = xname, \ + .id = (xid), \ + .capture = TOACODEC_STREAM(xname, "Capture", TOACODEC_OUT_CHMAX), \ + .ops = &g12a_toacodec_output_ops, \ +} + +static struct snd_soc_dai_driver g12a_toacodec_dai_drv[] = { + TOACODEC_INPUT("IN A", TOACODEC_IN_A), + TOACODEC_INPUT("IN B", TOACODEC_IN_B), + TOACODEC_INPUT("IN C", TOACODEC_IN_C), + TOACODEC_OUTPUT("OUT", TOACODEC_OUT), +}; + +static int g12a_toacodec_component_probe(struct snd_soc_component *c) +{ + /* Initialize the static clock parameters */ + return snd_soc_component_write(c, TOACODEC_CTRL0, + CTRL0_BLK_CAP_INV); +} + +static const struct snd_soc_dapm_route g12a_toacodec_routes[] = { + { "SRC", "I2S A", "IN A Playback" }, + { "SRC", "I2S B", "IN B Playback" }, + { "SRC", "I2S C", "IN C Playback" }, + { "OUT EN", "Switch", "SRC" }, + { "OUT Capture", NULL, "OUT EN" }, +}; + +static const struct snd_kcontrol_new g12a_toacodec_controls[] = { + SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 3, 0), +}; + +static const struct snd_soc_component_driver g12a_toacodec_component_drv = { + .probe = g12a_toacodec_component_probe, + .controls = g12a_toacodec_controls, + .num_controls = ARRAY_SIZE(g12a_toacodec_controls), + .dapm_widgets = g12a_toacodec_widgets, + .num_dapm_widgets = ARRAY_SIZE(g12a_toacodec_widgets), + .dapm_routes = g12a_toacodec_routes, + .num_dapm_routes = ARRAY_SIZE(g12a_toacodec_routes), + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config g12a_toacodec_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static const struct of_device_id g12a_toacodec_of_match[] = { + { .compatible = "amlogic,g12a-toacodec", }, + {} +}; +MODULE_DEVICE_TABLE(of, g12a_toacodec_of_match); + +static int g12a_toacodec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + void __iomem *regs; + struct regmap *map; + int ret; + + ret = device_reset(dev); + if (ret) + return ret; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + map = devm_regmap_init_mmio(dev, regs, &g12a_toacodec_regmap_cfg); + if (IS_ERR(map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(map)); + return PTR_ERR(map); + } + + return devm_snd_soc_register_component(dev, + &g12a_toacodec_component_drv, g12a_toacodec_dai_drv, + ARRAY_SIZE(g12a_toacodec_dai_drv)); +} + +static struct platform_driver g12a_toacodec_pdrv = { + .driver = { + .name = G12A_TOACODEC_DRV_NAME, + .of_match_table = g12a_toacodec_of_match, + }, + .probe = g12a_toacodec_probe, +}; +module_platform_driver(g12a_toacodec_pdrv); + +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_DESCRIPTION("Amlogic G12a To Internal DAC Codec Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c index 8a0db28a6a40..9b2b59536ced 100644 --- a/sound/soc/meson/g12a-tohdmitx.c +++ b/sound/soc/meson/g12a-tohdmitx.c @@ -13,112 +13,51 @@ #include <sound/soc-dai.h> #include <dt-bindings/sound/meson-g12a-tohdmitx.h> +#include "meson-codec-glue.h" #define G12A_TOHDMITX_DRV_NAME "g12a-tohdmitx" #define TOHDMITX_CTRL0 0x0 #define CTRL0_ENABLE_SHIFT 31 -#define CTRL0_I2S_DAT_SEL GENMASK(13, 12) +#define CTRL0_I2S_DAT_SEL_SHIFT 12 +#define CTRL0_I2S_DAT_SEL (0x3 << CTRL0_I2S_DAT_SEL_SHIFT) #define CTRL0_I2S_LRCLK_SEL GENMASK(9, 8) #define CTRL0_I2S_BLK_CAP_INV BIT(7) #define CTRL0_I2S_BCLK_O_INV BIT(6) #define CTRL0_I2S_BCLK_SEL GENMASK(5, 4) #define CTRL0_SPDIF_CLK_CAP_INV BIT(3) #define CTRL0_SPDIF_CLK_O_INV BIT(2) -#define CTRL0_SPDIF_SEL BIT(1) +#define CTRL0_SPDIF_SEL_SHIFT 1 +#define CTRL0_SPDIF_SEL (0x1 << CTRL0_SPDIF_SEL_SHIFT) #define CTRL0_SPDIF_CLK_SEL BIT(0) -struct g12a_tohdmitx_input { - struct snd_soc_pcm_stream params; - unsigned int fmt; -}; - -static struct snd_soc_dapm_widget * -g12a_tohdmitx_get_input(struct snd_soc_dapm_widget *w) -{ - struct snd_soc_dapm_path *p = NULL; - struct snd_soc_dapm_widget *in; - - snd_soc_dapm_widget_for_each_source_path(w, p) { - if (!p->connect) - continue; - - /* Check that we still are in the same component */ - if (snd_soc_dapm_to_component(w->dapm) != - snd_soc_dapm_to_component(p->source->dapm)) - continue; - - if (p->source->id == snd_soc_dapm_dai_in) - return p->source; - - in = g12a_tohdmitx_get_input(p->source); - if (in) - return in; - } - - return NULL; -} - -static struct g12a_tohdmitx_input * -g12a_tohdmitx_get_input_data(struct snd_soc_dapm_widget *w) -{ - struct snd_soc_dapm_widget *in = - g12a_tohdmitx_get_input(w); - struct snd_soc_dai *dai; - - if (WARN_ON(!in)) - return NULL; - - dai = in->priv; - - return dai->playback_dma_data; -} - static const char * const g12a_tohdmitx_i2s_mux_texts[] = { "I2S A", "I2S B", "I2S C", }; -static SOC_ENUM_SINGLE_EXT_DECL(g12a_tohdmitx_i2s_mux_enum, - g12a_tohdmitx_i2s_mux_texts); - -static int g12a_tohdmitx_get_input_val(struct snd_soc_component *component, - unsigned int mask) -{ - unsigned int val; - - snd_soc_component_read(component, TOHDMITX_CTRL0, &val); - return (val & mask) >> __ffs(mask); -} - -static int g12a_tohdmitx_i2s_mux_get_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_dapm_kcontrol_component(kcontrol); - - ucontrol->value.enumerated.item[0] = - g12a_tohdmitx_get_input_val(component, CTRL0_I2S_DAT_SEL); - - return 0; -} - static int g12a_tohdmitx_i2s_mux_put_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int mux = ucontrol->value.enumerated.item[0]; - unsigned int val = g12a_tohdmitx_get_input_val(component, - CTRL0_I2S_DAT_SEL); + unsigned int mux, changed; + + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); + changed = snd_soc_component_test_bits(component, e->reg, + CTRL0_I2S_DAT_SEL, + FIELD_PREP(CTRL0_I2S_DAT_SEL, + mux)); + + if (!changed) + return 0; /* Force disconnect of the mux while updating */ - if (val != mux) - snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); + snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); - snd_soc_component_update_bits(component, TOHDMITX_CTRL0, + snd_soc_component_update_bits(component, e->reg, CTRL0_I2S_DAT_SEL | CTRL0_I2S_LRCLK_SEL | CTRL0_I2S_BCLK_SEL, @@ -131,30 +70,19 @@ static int g12a_tohdmitx_i2s_mux_put_enum(struct snd_kcontrol *kcontrol, return 0; } +static SOC_ENUM_SINGLE_DECL(g12a_tohdmitx_i2s_mux_enum, TOHDMITX_CTRL0, + CTRL0_I2S_DAT_SEL_SHIFT, + g12a_tohdmitx_i2s_mux_texts); + static const struct snd_kcontrol_new g12a_tohdmitx_i2s_mux = SOC_DAPM_ENUM_EXT("I2S Source", g12a_tohdmitx_i2s_mux_enum, - g12a_tohdmitx_i2s_mux_get_enum, + snd_soc_dapm_get_enum_double, g12a_tohdmitx_i2s_mux_put_enum); static const char * const g12a_tohdmitx_spdif_mux_texts[] = { "SPDIF A", "SPDIF B", }; -static SOC_ENUM_SINGLE_EXT_DECL(g12a_tohdmitx_spdif_mux_enum, - g12a_tohdmitx_spdif_mux_texts); - -static int g12a_tohdmitx_spdif_mux_get_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_dapm_kcontrol_component(kcontrol); - - ucontrol->value.enumerated.item[0] = - g12a_tohdmitx_get_input_val(component, CTRL0_SPDIF_SEL); - - return 0; -} - static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -163,13 +91,18 @@ static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int mux = ucontrol->value.enumerated.item[0]; - unsigned int val = g12a_tohdmitx_get_input_val(component, - CTRL0_SPDIF_SEL); + unsigned int mux, changed; + + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); + changed = snd_soc_component_test_bits(component, TOHDMITX_CTRL0, + CTRL0_SPDIF_SEL, + FIELD_PREP(CTRL0_SPDIF_SEL, mux)); + + if (!changed) + return 0; /* Force disconnect of the mux while updating */ - if (val != mux) - snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); + snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); snd_soc_component_update_bits(component, TOHDMITX_CTRL0, CTRL0_SPDIF_SEL | @@ -182,9 +115,13 @@ static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol, return 0; } +static SOC_ENUM_SINGLE_DECL(g12a_tohdmitx_spdif_mux_enum, TOHDMITX_CTRL0, + CTRL0_SPDIF_SEL_SHIFT, + g12a_tohdmitx_spdif_mux_texts); + static const struct snd_kcontrol_new g12a_tohdmitx_spdif_mux = SOC_DAPM_ENUM_EXT("SPDIF Source", g12a_tohdmitx_spdif_mux_enum, - g12a_tohdmitx_spdif_mux_get_enum, + snd_soc_dapm_get_enum_double, g12a_tohdmitx_spdif_mux_put_enum); static const struct snd_kcontrol_new g12a_tohdmitx_out_enable = @@ -202,83 +139,13 @@ static const struct snd_soc_dapm_widget g12a_tohdmitx_widgets[] = { &g12a_tohdmitx_out_enable), }; -static int g12a_tohdmitx_input_probe(struct snd_soc_dai *dai) -{ - struct g12a_tohdmitx_input *data; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - dai->playback_dma_data = data; - return 0; -} - -static int g12a_tohdmitx_input_remove(struct snd_soc_dai *dai) -{ - kfree(dai->playback_dma_data); - return 0; -} - -static int g12a_tohdmitx_input_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct g12a_tohdmitx_input *data = dai->playback_dma_data; - - data->params.rates = snd_pcm_rate_to_rate_bit(params_rate(params)); - data->params.rate_min = params_rate(params); - data->params.rate_max = params_rate(params); - data->params.formats = 1 << params_format(params); - data->params.channels_min = params_channels(params); - data->params.channels_max = params_channels(params); - data->params.sig_bits = dai->driver->playback.sig_bits; - - return 0; -} - - -static int g12a_tohdmitx_input_set_fmt(struct snd_soc_dai *dai, - unsigned int fmt) -{ - struct g12a_tohdmitx_input *data = dai->playback_dma_data; - - /* Save the source stream format for the downstream link */ - data->fmt = fmt; - return 0; -} - -static int g12a_tohdmitx_output_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct g12a_tohdmitx_input *in_data = - g12a_tohdmitx_get_input_data(dai->capture_widget); - - if (!in_data) - return -ENODEV; - - if (WARN_ON(!rtd->dai_link->params)) { - dev_warn(dai->dev, "codec2codec link expected\n"); - return -EINVAL; - } - - /* Replace link params with the input params */ - rtd->dai_link->params = &in_data->params; - - if (!in_data->fmt) - return 0; - - return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt); -} - static const struct snd_soc_dai_ops g12a_tohdmitx_input_ops = { - .hw_params = g12a_tohdmitx_input_hw_params, - .set_fmt = g12a_tohdmitx_input_set_fmt, + .hw_params = meson_codec_glue_input_hw_params, + .set_fmt = meson_codec_glue_input_set_fmt, }; static const struct snd_soc_dai_ops g12a_tohdmitx_output_ops = { - .startup = g12a_tohdmitx_output_startup, + .startup = meson_codec_glue_output_startup, }; #define TOHDMITX_SPDIF_FORMATS \ @@ -305,8 +172,8 @@ static const struct snd_soc_dai_ops g12a_tohdmitx_output_ops = { .id = (xid), \ .playback = TOHDMITX_STREAM(xname, "Playback", xfmt, xchmax), \ .ops = &g12a_tohdmitx_input_ops, \ - .probe = g12a_tohdmitx_input_probe, \ - .remove = g12a_tohdmitx_input_remove, \ + .probe = meson_codec_glue_input_dai_probe, \ + .remove = meson_codec_glue_input_dai_remove, \ } #define TOHDMITX_OUT(xname, xid, xfmt, xchmax) { \ diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c new file mode 100644 index 000000000000..7b01dcb73e5e --- /dev/null +++ b/sound/soc/meson/gx-card.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "meson-card.h" + +struct gx_dai_link_i2s_data { + unsigned int mclk_fs; +}; + +/* + * Base params for the codec to codec links + * Those will be over-written by the CPU side of the link + */ +static const struct snd_soc_pcm_stream codec_params = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 5525, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, +}; + +static int gx_card_i2s_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct gx_dai_link_i2s_data *be = + (struct gx_dai_link_i2s_data *)priv->link_data[rtd->num]; + + return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs); +} + +static const struct snd_soc_ops gx_card_i2s_be_ops = { + .hw_params = gx_card_i2s_be_hw_params, +}; + +static int gx_card_parse_i2s(struct snd_soc_card *card, + struct device_node *node, + int *index) +{ + struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = &card->dai_link[*index]; + struct gx_dai_link_i2s_data *be; + + /* Allocate i2s link parameters */ + be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); + if (!be) + return -ENOMEM; + priv->link_data[*index] = be; + + /* Setup i2s link */ + link->ops = &gx_card_i2s_be_ops; + link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node); + + of_property_read_u32(node, "mclk-fs", &be->mclk_fs); + + return 0; +} + +static int gx_card_cpu_identify(struct snd_soc_dai_link_component *c, + char *match) +{ + if (of_device_is_compatible(c->of_node, DT_PREFIX "aiu")) { + if (strstr(c->dai_name, match)) + return 1; + } + + /* dai not matched */ + return 0; +} + +static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, + int *index) +{ + struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; + struct snd_soc_dai_link_component *cpu; + int ret; + + cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); + if (!cpu) + return -ENOMEM; + + dai_link->cpus = cpu; + dai_link->num_cpus = 1; + + ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node, + &dai_link->cpus->dai_name); + if (ret) + return ret; + + if (gx_card_cpu_identify(dai_link->cpus, "FIFO")) + ret = meson_card_set_fe_link(card, dai_link, np, true); + else + ret = meson_card_set_be_link(card, dai_link, np); + + if (ret) + return ret; + + /* Check if the cpu is the i2s encoder and parse i2s data */ + if (gx_card_cpu_identify(dai_link->cpus, "I2S Encoder")) + ret = gx_card_parse_i2s(card, np, index); + + /* Or apply codec to codec params if necessary */ + else if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL")) + dai_link->params = &codec_params; + + return ret; +} + +static const struct meson_card_match_data gx_card_match_data = { + .add_link = gx_card_add_link, +}; + +static const struct of_device_id gx_card_of_match[] = { + { + .compatible = "amlogic,gx-sound-card", + .data = &gx_card_match_data, + }, {} +}; +MODULE_DEVICE_TABLE(of, gx_card_of_match); + +static struct platform_driver gx_card_pdrv = { + .probe = meson_card_probe, + .remove = meson_card_remove, + .driver = { + .name = "gx-sound-card", + .of_match_table = gx_card_of_match, + }, +}; +module_platform_driver(gx_card_pdrv); + +MODULE_DESCRIPTION("Amlogic GX ALSA machine driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c new file mode 100644 index 000000000000..2ca8c98e204f --- /dev/null +++ b/sound/soc/meson/meson-card-utils.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/soc.h> + +#include "meson-card.h" + +int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + unsigned int mclk_fs) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; + unsigned int mclk; + int ret, i; + + if (!mclk_fs) + return 0; + + mclk = params_rate(params) * mclk_fs; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } + + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk); + +int meson_card_reallocate_links(struct snd_soc_card *card, + unsigned int num_links) +{ + struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *links; + void **ldata; + + links = krealloc(priv->card.dai_link, + num_links * sizeof(*priv->card.dai_link), + GFP_KERNEL | __GFP_ZERO); + ldata = krealloc(priv->link_data, + num_links * sizeof(*priv->link_data), + GFP_KERNEL | __GFP_ZERO); + + if (!links || !ldata) { + dev_err(priv->card.dev, "failed to allocate links\n"); + return -ENOMEM; + } + + priv->card.dai_link = links; + priv->link_data = ldata; + priv->card.num_links = num_links; + return 0; +} +EXPORT_SYMBOL_GPL(meson_card_reallocate_links); + +int meson_card_parse_dai(struct snd_soc_card *card, + struct device_node *node, + struct device_node **dai_of_node, + const char **dai_name) +{ + struct of_phandle_args args; + int ret; + + if (!dai_name || !dai_of_node || !node) + return -EINVAL; + + 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; + } + *dai_of_node = args.np; + + return snd_soc_get_dai_name(&args, dai_name); +} +EXPORT_SYMBOL_GPL(meson_card_parse_dai); + +static int meson_card_set_link_name(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + const char *prefix) +{ + char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", + prefix, node->full_name); + if (!name) + return -ENOMEM; + + link->name = name; + link->stream_name = name; + + return 0; +} + +unsigned int meson_card_parse_daifmt(struct device_node *node, + struct device_node *cpu_node) +{ + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + unsigned int daifmt; + + daifmt = snd_soc_of_parse_daifmt(node, DT_PREFIX, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + + /* If no master is provided, default to cpu master */ + if (!bitclkmaster || bitclkmaster == cpu_node) { + daifmt |= (!framemaster || framemaster == cpu_node) ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; + } else { + daifmt |= (!framemaster || framemaster == cpu_node) ? + SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; + } + + of_node_put(bitclkmaster); + of_node_put(framemaster); + + return daifmt; +} +EXPORT_SYMBOL_GPL(meson_card_parse_daifmt); + +int meson_card_set_be_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node) +{ + struct snd_soc_dai_link_component *codec; + struct device_node *np; + int ret, num_codecs; + + link->no_pcm = 1; + link->dpcm_playback = 1; + link->dpcm_capture = 1; + + num_codecs = of_get_child_count(node); + if (!num_codecs) { + dev_err(card->dev, "be link %s has no codec\n", + node->full_name); + return -EINVAL; + } + + codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + link->codecs = codec; + link->num_codecs = num_codecs; + + for_each_child_of_node(node, np) { + ret = meson_card_parse_dai(card, np, &codec->of_node, + &codec->dai_name); + if (ret) { + of_node_put(np); + return ret; + } + + codec++; + } + + ret = meson_card_set_link_name(card, link, node, "be"); + if (ret) + dev_err(card->dev, "error setting %pOFn link name\n", np); + + return ret; +} +EXPORT_SYMBOL_GPL(meson_card_set_be_link); + +int meson_card_set_fe_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + bool is_playback) +{ + struct snd_soc_dai_link_component *codec; + + codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + link->codecs = codec; + link->num_codecs = 1; + + link->dynamic = 1; + link->dpcm_merged_format = 1; + link->dpcm_merged_chan = 1; + link->dpcm_merged_rate = 1; + link->codecs->dai_name = "snd-soc-dummy-dai"; + link->codecs->name = "snd-soc-dummy"; + + if (is_playback) + link->dpcm_playback = 1; + else + link->dpcm_capture = 1; + + return meson_card_set_link_name(card, link, node, "fe"); +} +EXPORT_SYMBOL_GPL(meson_card_set_fe_link); + +static int meson_card_add_links(struct snd_soc_card *card) +{ + struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct device_node *node = card->dev->of_node; + struct device_node *np; + int num, i, ret; + + num = of_get_child_count(node); + if (!num) { + dev_err(card->dev, "card has no links\n"); + return -EINVAL; + } + + ret = meson_card_reallocate_links(card, num); + if (ret) + return ret; + + i = 0; + for_each_child_of_node(node, np) { + ret = priv->match_data->add_link(card, np, &i); + if (ret) { + of_node_put(np); + return ret; + } + + i++; + } + + return 0; +} + +static int meson_card_parse_of_optional(struct snd_soc_card *card, + const char *propname, + int (*func)(struct snd_soc_card *c, + const char *p)) +{ + /* If property is not provided, don't fail ... */ + if (!of_property_read_bool(card->dev->of_node, propname)) + return 0; + + /* ... but do fail if it is provided and the parsing fails */ + return func(card, propname); +} + +static int meson_card_add_aux_devices(struct snd_soc_card *card) +{ + struct device_node *node = card->dev->of_node; + struct snd_soc_aux_dev *aux; + int num, i; + + num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); + if (num == -ENOENT) { + return 0; + } else if (num < 0) { + dev_err(card->dev, "error getting auxiliary devices: %d\n", + num); + return num; + } + + aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); + if (!aux) + return -ENOMEM; + card->aux_dev = aux; + card->num_aux_devs = num; + + for_each_card_pre_auxs(card, i, aux) { + aux->dlc.of_node = + of_parse_phandle(node, "audio-aux-devs", i); + if (!aux->dlc.of_node) + return -EINVAL; + } + + return 0; +} + +static void meson_card_clean_references(struct meson_card *priv) +{ + struct snd_soc_card *card = &priv->card; + struct snd_soc_dai_link *link; + struct snd_soc_dai_link_component *codec; + struct snd_soc_aux_dev *aux; + int i, j; + + if (card->dai_link) { + for_each_card_prelinks(card, i, link) { + if (link->cpus) + of_node_put(link->cpus->of_node); + for_each_link_codecs(link, j, codec) + of_node_put(codec->of_node); + } + } + + if (card->aux_dev) { + for_each_card_pre_auxs(card, i, aux) + of_node_put(aux->dlc.of_node); + } + + kfree(card->dai_link); + kfree(priv->link_data); +} + +int meson_card_probe(struct platform_device *pdev) +{ + const struct meson_card_match_data *data; + struct device *dev = &pdev->dev; + struct meson_card *priv; + int ret; + + data = of_device_get_match_data(dev); + if (!data) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + snd_soc_card_set_drvdata(&priv->card, priv); + + priv->card.owner = THIS_MODULE; + priv->card.dev = dev; + priv->match_data = data; + + ret = snd_soc_of_parse_card_name(&priv->card, "model"); + if (ret < 0) + return ret; + + ret = meson_card_parse_of_optional(&priv->card, "audio-routing", + snd_soc_of_parse_audio_routing); + if (ret) { + dev_err(dev, "error while parsing routing\n"); + return ret; + } + + ret = meson_card_parse_of_optional(&priv->card, "audio-widgets", + snd_soc_of_parse_audio_simple_widgets); + if (ret) { + dev_err(dev, "error while parsing widgets\n"); + return ret; + } + + ret = meson_card_add_links(&priv->card); + if (ret) + goto out_err; + + ret = meson_card_add_aux_devices(&priv->card); + if (ret) + goto out_err; + + ret = devm_snd_soc_register_card(dev, &priv->card); + if (ret) + goto out_err; + + return 0; + +out_err: + meson_card_clean_references(priv); + return ret; +} +EXPORT_SYMBOL_GPL(meson_card_probe); + +int meson_card_remove(struct platform_device *pdev) +{ + struct meson_card *priv = platform_get_drvdata(pdev); + + meson_card_clean_references(priv); + + return 0; +} +EXPORT_SYMBOL_GPL(meson_card_remove); + +MODULE_DESCRIPTION("Amlogic Sound Card Utils"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/meson-card.h b/sound/soc/meson/meson-card.h new file mode 100644 index 000000000000..74314071c80d --- /dev/null +++ b/sound/soc/meson/meson-card.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_SND_CARD_H +#define _MESON_SND_CARD_H + +struct device_node; +struct platform_device; + +struct snd_soc_card; +struct snd_pcm_substream; +struct snd_pcm_hw_params; + +#define DT_PREFIX "amlogic," + +struct meson_card_match_data { + int (*add_link)(struct snd_soc_card *card, + struct device_node *node, + int *index); +}; + +struct meson_card { + const struct meson_card_match_data *match_data; + struct snd_soc_card card; + void **link_data; +}; + +unsigned int meson_card_parse_daifmt(struct device_node *node, + struct device_node *cpu_node); + +int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + unsigned int mclk_fs); + +int meson_card_reallocate_links(struct snd_soc_card *card, + unsigned int num_links); +int meson_card_parse_dai(struct snd_soc_card *card, + struct device_node *node, + struct device_node **dai_of_node, + const char **dai_name); +int meson_card_set_be_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node); +int meson_card_set_fe_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + bool is_playback); + +int meson_card_probe(struct platform_device *pdev); +int meson_card_remove(struct platform_device *pdev); + +#endif /* _MESON_SND_CARD_H */ diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c new file mode 100644 index 000000000000..524a33472337 --- /dev/null +++ b/sound/soc/meson/meson-codec-glue.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2019 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/module.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "meson-codec-glue.h" + +static struct snd_soc_dapm_widget * +meson_codec_glue_get_input(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dapm_widget *in; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->connect) + continue; + + /* Check that we still are in the same component */ + if (snd_soc_dapm_to_component(w->dapm) != + snd_soc_dapm_to_component(p->source->dapm)) + continue; + + if (p->source->id == snd_soc_dapm_dai_in) + return p->source; + + in = meson_codec_glue_get_input(p->source); + if (in) + return in; + } + + return NULL; +} + +static void meson_codec_glue_input_set_data(struct snd_soc_dai *dai, + struct meson_codec_glue_input *data) +{ + dai->playback_dma_data = data; +} + +struct meson_codec_glue_input * +meson_codec_glue_input_get_data(struct snd_soc_dai *dai) +{ + return dai->playback_dma_data; +} +EXPORT_SYMBOL_GPL(meson_codec_glue_input_get_data); + +static struct meson_codec_glue_input * +meson_codec_glue_output_get_input_data(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_widget *in = + meson_codec_glue_get_input(w); + struct snd_soc_dai *dai; + + if (WARN_ON(!in)) + return NULL; + + dai = in->priv; + + return meson_codec_glue_input_get_data(dai); +} + +int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct meson_codec_glue_input *data = + meson_codec_glue_input_get_data(dai); + + data->params.rates = snd_pcm_rate_to_rate_bit(params_rate(params)); + data->params.rate_min = params_rate(params); + data->params.rate_max = params_rate(params); + data->params.formats = 1ULL << (__force int) params_format(params); + data->params.channels_min = params_channels(params); + data->params.channels_max = params_channels(params); + data->params.sig_bits = dai->driver->playback.sig_bits; + + return 0; +} +EXPORT_SYMBOL_GPL(meson_codec_glue_input_hw_params); + +int meson_codec_glue_input_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct meson_codec_glue_input *data = + meson_codec_glue_input_get_data(dai); + + /* Save the source stream format for the downstream link */ + data->fmt = fmt; + return 0; +} +EXPORT_SYMBOL_GPL(meson_codec_glue_input_set_fmt); + +int meson_codec_glue_output_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct meson_codec_glue_input *in_data = + meson_codec_glue_output_get_input_data(dai->capture_widget); + + if (!in_data) + return -ENODEV; + + if (WARN_ON(!rtd->dai_link->params)) { + dev_warn(dai->dev, "codec2codec link expected\n"); + return -EINVAL; + } + + /* Replace link params with the input params */ + rtd->dai_link->params = &in_data->params; + + if (!in_data->fmt) + return 0; + + return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt); +} +EXPORT_SYMBOL_GPL(meson_codec_glue_output_startup); + +int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai) +{ + struct meson_codec_glue_input *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + meson_codec_glue_input_set_data(dai, data); + return 0; +} +EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_probe); + +int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai) +{ + struct meson_codec_glue_input *data = + meson_codec_glue_input_get_data(dai); + + kfree(data); + return 0; +} +EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_remove); + +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_DESCRIPTION("Amlogic Codec Glue Helpers"); +MODULE_LICENSE("GPL v2"); + diff --git a/sound/soc/meson/meson-codec-glue.h b/sound/soc/meson/meson-codec-glue.h new file mode 100644 index 000000000000..07f99446c0c6 --- /dev/null +++ b/sound/soc/meson/meson-codec-glue.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2018 Baylibre SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_CODEC_GLUE_H +#define _MESON_CODEC_GLUE_H + +#include <sound/soc.h> + +struct meson_codec_glue_input { + struct snd_soc_pcm_stream params; + unsigned int fmt; +}; + +/* Input helpers */ +struct meson_codec_glue_input * +meson_codec_glue_input_get_data(struct snd_soc_dai *dai); +int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +int meson_codec_glue_input_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt); +int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai); +int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai); + +/* Output helpers */ +int meson_codec_glue_output_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +#endif /* _MESON_CODEC_GLUE_H */ diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c new file mode 100644 index 000000000000..56d2592c16d5 --- /dev/null +++ b/sound/soc/meson/t9015.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define BLOCK_EN 0x00 +#define LORN_EN 0 +#define LORP_EN 1 +#define LOLN_EN 2 +#define LOLP_EN 3 +#define DACR_EN 4 +#define DACL_EN 5 +#define DACR_INV 20 +#define DACL_INV 21 +#define DACR_SRC 22 +#define DACL_SRC 23 +#define REFP_BUF_EN BIT(12) +#define BIAS_CURRENT_EN BIT(13) +#define VMID_GEN_FAST BIT(14) +#define VMID_GEN_EN BIT(15) +#define I2S_MODE BIT(30) +#define VOL_CTRL0 0x04 +#define GAIN_H 31 +#define GAIN_L 23 +#define VOL_CTRL1 0x08 +#define DAC_MONO 8 +#define RAMP_RATE 10 +#define VC_RAMP_MODE 12 +#define MUTE_MODE 13 +#define UNMUTE_MODE 14 +#define DAC_SOFT_MUTE 15 +#define DACR_VC 16 +#define DACL_VC 24 +#define LINEOUT_CFG 0x0c +#define LORN_POL 0 +#define LORP_POL 4 +#define LOLN_POL 8 +#define LOLP_POL 12 +#define POWER_CFG 0x10 + +struct t9015 { + struct clk *pclk; + struct regulator *avdd; +}; + +static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + unsigned int val; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + val = I2S_MODE; + break; + + case SND_SOC_DAIFMT_CBS_CFS: + val = 0; + break; + + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, BLOCK_EN, I2S_MODE, val); + + if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) && + ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_LEFT_J)) + return -EINVAL; + + return 0; +} + +static const struct snd_soc_dai_ops t9015_dai_ops = { + .set_fmt = t9015_dai_set_fmt, +}; + +static struct snd_soc_dai_driver t9015_dai = { + .name = "t9015-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = (SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &t9015_dai_ops, +}; + +static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0); + +static const char * const ramp_rate_txt[] = { "Fast", "Slow" }; +static SOC_ENUM_SINGLE_DECL(ramp_rate_enum, VOL_CTRL1, RAMP_RATE, + ramp_rate_txt); + +static const char * const dacr_in_txt[] = { "Right", "Left" }; +static SOC_ENUM_SINGLE_DECL(dacr_in_enum, BLOCK_EN, DACR_SRC, dacr_in_txt); + +static const char * const dacl_in_txt[] = { "Left", "Right" }; +static SOC_ENUM_SINGLE_DECL(dacl_in_enum, BLOCK_EN, DACL_SRC, dacl_in_txt); + +static const char * const mono_txt[] = { "Stereo", "Mono"}; +static SOC_ENUM_SINGLE_DECL(mono_enum, VOL_CTRL1, DAC_MONO, mono_txt); + +static const struct snd_kcontrol_new t9015_snd_controls[] = { + /* Volume Controls */ + SOC_ENUM("Playback Channel Mode", mono_enum), + SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1), + SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC, + 0xff, 0, dac_vol_tlv), + + /* Ramp Controls */ + SOC_ENUM("Ramp Rate", ramp_rate_enum), + SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0), + SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0), + SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0), +}; + +static const struct snd_kcontrol_new t9015_right_dac_mux = + SOC_DAPM_ENUM("Right DAC Source", dacr_in_enum); +static const struct snd_kcontrol_new t9015_left_dac_mux = + SOC_DAPM_ENUM("Left DAC Source", dacl_in_enum); + +static const struct snd_soc_dapm_widget t9015_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0, + &t9015_right_dac_mux), + SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0, + &t9015_left_dac_mux), + SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0), + SND_SOC_DAPM_DAC("Left DAC", NULL, BLOCK_EN, DACL_EN, 0), + SND_SOC_DAPM_OUT_DRV("Right- Driver", BLOCK_EN, LORN_EN, 0, + NULL, 0), + SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0, + NULL, 0), + SND_SOC_DAPM_OUT_DRV("Left- Driver", BLOCK_EN, LOLN_EN, 0, + NULL, 0), + SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN, LOLP_EN, 0, + NULL, 0), + SND_SOC_DAPM_OUTPUT("LORN"), + SND_SOC_DAPM_OUTPUT("LORP"), + SND_SOC_DAPM_OUTPUT("LOLN"), + SND_SOC_DAPM_OUTPUT("LOLP"), +}; + +static const struct snd_soc_dapm_route t9015_dapm_routes[] = { + { "Right IN", NULL, "Playback" }, + { "Left IN", NULL, "Playback" }, + { "Right DAC Sel", "Right", "Right IN" }, + { "Right DAC Sel", "Left", "Left IN" }, + { "Left DAC Sel", "Right", "Right IN" }, + { "Left DAC Sel", "Left", "Left IN" }, + { "Right DAC", NULL, "Right DAC Sel" }, + { "Left DAC", NULL, "Left DAC Sel" }, + { "Right- Driver", NULL, "Right DAC" }, + { "Right+ Driver", NULL, "Right DAC" }, + { "Left- Driver", NULL, "Left DAC" }, + { "Left+ Driver", NULL, "Left DAC" }, + { "LORN", NULL, "Right- Driver", }, + { "LORP", NULL, "Right+ Driver", }, + { "LOLN", NULL, "Left- Driver", }, + { "LOLP", NULL, "Left+ Driver", }, +}; + +static int t9015_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct t9015 *priv = snd_soc_component_get_drvdata(component); + enum snd_soc_bias_level now = + snd_soc_component_get_bias_level(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_update_bits(component, BLOCK_EN, + BIAS_CURRENT_EN, + BIAS_CURRENT_EN); + break; + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, BLOCK_EN, + BIAS_CURRENT_EN, + 0); + break; + case SND_SOC_BIAS_STANDBY: + ret = regulator_enable(priv->avdd); + if (ret) { + dev_err(component->dev, "AVDD enable failed\n"); + return ret; + } + + if (now == SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, BLOCK_EN, + VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN, + VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN); + + mdelay(200); + snd_soc_component_update_bits(component, BLOCK_EN, + VMID_GEN_FAST, + 0); + } + + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, BLOCK_EN, + VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN, + 0); + + regulator_disable(priv->avdd); + break; + } + + return 0; +} + +static const struct snd_soc_component_driver t9015_codec_driver = { + .set_bias_level = t9015_set_bias_level, + .controls = t9015_snd_controls, + .num_controls = ARRAY_SIZE(t9015_snd_controls), + .dapm_widgets = t9015_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(t9015_dapm_widgets), + .dapm_routes = t9015_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(t9015_dapm_routes), + .suspend_bias_off = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config t9015_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = POWER_CFG, +}; + +static int t9015_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct t9015 *priv; + void __iomem *regs; + struct regmap *regmap; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + 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); + } + + 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); + } + + ret = clk_prepare_enable(priv->pclk); + if (ret) { + dev_err(dev, "core clock enable failed\n"); + return ret; + } + + ret = devm_add_action_or_reset(dev, + (void(*)(void *))clk_disable_unprepare, + priv->pclk); + if (ret) + return ret; + + ret = device_reset(dev); + if (ret) { + dev_err(dev, "reset failed\n"); + return ret; + } + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) { + dev_err(dev, "register map failed\n"); + return PTR_ERR(regs); + } + + regmap = devm_regmap_init_mmio(dev, regs, &t9015_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(regmap); + } + + /* + * Initialize output polarity: + * ATM the output polarity is fixed but in the future it might useful + * to add DT property to set this depending on the platform needs + */ + regmap_write(regmap, LINEOUT_CFG, 0x1111); + + return devm_snd_soc_register_component(dev, &t9015_codec_driver, + &t9015_dai, 1); +} + +static const struct of_device_id t9015_ids[] = { + { .compatible = "amlogic,t9015", }, + { } +}; +MODULE_DEVICE_TABLE(of, t9015_ids); + +static struct platform_driver t9015_driver = { + .driver = { + .name = "t9015-codec", + .of_match_table = of_match_ptr(t9015_ids), + }, + .probe = t9015_probe, +}; + +module_platform_driver(t9015_driver); + +MODULE_DESCRIPTION("ASoC Amlogic T9015 codec driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 9841e1da9782..f46d7aca8cf6 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -20,8 +20,8 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int rate = params_rate(params); u32 mclk; int ret; diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 295cfffa4646..d4c0f580a565 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -81,6 +81,9 @@ config SND_PXA2XX_SOC_TOSA depends on SND_PXA2XX_SOC && MACH_TOSA depends on MFD_TC6393XB depends on AC97_BUS=n + select REGMAP + select AC97_BUS_NEW + select AC97_BUS_COMPAT select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help @@ -91,6 +94,9 @@ config SND_PXA2XX_SOC_E740 tristate "SoC AC97 Audio support for e740" depends on SND_PXA2XX_SOC && MACH_E740 depends on AC97_BUS=n + select REGMAP + select AC97_BUS_NEW + select AC97_BUS_COMPAT select SND_SOC_WM9705 select SND_PXA2XX_SOC_AC97 help @@ -101,6 +107,7 @@ config SND_PXA2XX_SOC_E750 tristate "SoC AC97 Audio support for e750" depends on SND_PXA2XX_SOC && MACH_E750 depends on AC97_BUS=n + select REGMAP select SND_SOC_WM9705 select SND_PXA2XX_SOC_AC97 help @@ -111,7 +118,10 @@ config SND_PXA2XX_SOC_E800 tristate "SoC AC97 Audio support for e800" depends on SND_PXA2XX_SOC && MACH_E800 depends on AC97_BUS=n + select REGMAP select SND_SOC_WM9712 + select AC97_BUS_NEW + select AC97_BUS_COMPAT select SND_PXA2XX_SOC_AC97 help Say Y if you want to add support for SoC audio on the @@ -122,6 +132,9 @@ config SND_PXA2XX_SOC_EM_X270 depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \ MACH_CM_X300) depends on AC97_BUS=n + select REGMAP + select AC97_BUS_NEW + select AC97_BUS_COMPAT select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help @@ -133,6 +146,9 @@ config SND_PXA2XX_SOC_PALM27X depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || \ MACH_PALMT5 || MACH_PALMTE2) depends on AC97_BUS=n + select REGMAP + select AC97_BUS_NEW + select AC97_BUS_COMPAT select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help @@ -163,7 +179,10 @@ config SND_SOC_ZYLONITE tristate "SoC Audio support for Marvell Zylonite" depends on SND_PXA2XX_SOC && MACH_ZYLONITE depends on AC97_BUS=n + select AC97_BUS_NEW + select AC97_BUS_COMPAT select SND_PXA2XX_SOC_AC97 + select REGMAP select SND_PXA_SOC_SSP select SND_SOC_WM9713 help @@ -193,6 +212,9 @@ config SND_PXA2XX_SOC_MIOA701 tristate "SoC Audio support for MIO A701" depends on SND_PXA2XX_SOC && MACH_MIOA701 depends on AC97_BUS=n + select REGMAP + select AC97_BUS_NEW + select AC97_BUS_COMPAT select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9713 help diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 53b1435ced3f..016a91199485 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -44,8 +44,8 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int freq_out, sspa_mclk, sysclk; if (params_rate(params) > 11025) { diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index d81082323fb4..6fbef9a0afa7 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -116,8 +116,8 @@ static int corgi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int clk = 0; int ret = 0; diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c index 0139343dbcce..b4da9a9a6521 100644 --- a/sound/soc/pxa/hx4700.c +++ b/sound/soc/pxa/hx4700.c @@ -54,8 +54,8 @@ static int hx4700_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret = 0; /* set the I2S system clock as output */ diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c index 514e17724fc3..3014e8244ab4 100644 --- a/sound/soc/pxa/imote2.c +++ b/sound/soc/pxa/imote2.c @@ -12,8 +12,8 @@ static int imote2_asoc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int clk = 0; int ret; diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 6483cff5b73d..e4c818f4cd62 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -83,8 +83,8 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int width; int ret = 0; @@ -121,8 +121,8 @@ static int magician_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret = 0; /* set codec DAI configuration */ @@ -358,10 +358,10 @@ static int __init magician_init(void) adapter = i2c_get_adapter(0); if (!adapter) return -ENODEV; - client = i2c_new_device(adapter, i2c_board_info); + client = i2c_new_client_device(adapter, i2c_board_info); i2c_put_adapter(adapter); - if (!client) - return -ENODEV; + if (IS_ERR(client)) + return PTR_ERR(client); ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER"); if (ret) diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 76e054d514a8..bf27b277c01f 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -73,7 +73,7 @@ static int rear_amp_event(struct snd_soc_dapm_widget *widget, struct snd_soc_component *component; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - component = rtd->codec_dai->component; + component = asoc_rtd_to_codec(rtd, 0)->component; return rear_amp_power(component, SND_SOC_DAPM_EVENT_ON(event)); } @@ -117,7 +117,7 @@ static const struct snd_soc_dapm_route audio_map[] = { static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; /* Prepare GPIO8 for rear speaker amplifier */ snd_soc_component_update_bits(component, AC97_GPIO_CFG, 0x100, 0x100); diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 287b5da739e5..3fe6c4c5a3ab 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -112,7 +112,7 @@ static int mmp_pcm_open(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct platform_device *pdev = to_platform_device(component->dev); - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct mmp_dma_data dma_data; struct resource *r; diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index e701637a9ae9..3548a2634a63 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -251,7 +251,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai); struct ssp_device *sspa = sspa_priv->sspa; struct snd_dmaengine_dai_dma_data *dma_params; diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 59ef04d0467a..287984a564c8 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -90,8 +90,8 @@ static int poodle_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int clk = 0; int ret = 0; diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 5f1c477b5833..9a32bf72127a 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -96,7 +96,7 @@ static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); if (IS_ERR(clk_i2s)) return PTR_ERR(clk_i2s); diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index f7babffb7228..6d8174f62935 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -117,8 +117,8 @@ static int spitz_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int clk = 0; int ret = 0; diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index d8f79e2266b1..d5f2961b1a3e 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -61,7 +61,7 @@ static const struct snd_soc_dapm_route ttc_audio_map[] = { static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; /* Headset jack detection */ snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE | diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index f9a33cb36f5b..6eee1aefc89a 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -34,8 +34,8 @@ static int z2_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int clk = 0; int ret = 0; diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index 567dc133ea92..447b59b8bd33 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -66,7 +66,7 @@ static const struct snd_soc_dapm_route audio_map[] = { static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd) { if (clk_pout) - snd_soc_dai_set_pll(rtd->codec_dai, 0, 0, + snd_soc_dai_set_pll(asoc_rtd_to_codec(rtd, 0), 0, 0, clk_get_rate(pout), 0); return 0; @@ -76,8 +76,8 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int wm9713_div = 0; int ret = 0; int rate = params_rate(params); diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 6530d2462a9e..f51b28d1b94d 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -99,7 +99,7 @@ config SND_SOC_MSM8996 config SND_SOC_SDM845 tristate "SoC Machine driver for SDM845 boards" - depends on QCOM_APR && CROS_EC && I2C + depends on QCOM_APR && CROS_EC && I2C && SOUNDWIRE select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON select SND_SOC_RT5663 diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index ac75838bbfab..2ef090f4af9e 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -33,9 +33,9 @@ struct apq8016_sbc_data { static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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_dai_link *dai_link = rtd->dai_link; struct snd_soc_card *card = rtd->card; struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card); int i, rval; @@ -90,10 +90,9 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) pdata->jack_setup = true; } - for (i = 0 ; i < dai_link->num_codecs; i++) { - struct snd_soc_dai *dai = rtd->codec_dais[i]; + for_each_rtd_codec_dais(rtd, i, codec_dai) { - component = dai->component; + component = codec_dai->component; /* Set default mclk for internal codec */ rval = snd_soc_component_set_sysclk(component, 0, 0, DEFAULT_MCLK_RATE, SND_SOC_CLOCK_IN); diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c index 94363fd6846a..d55e3ad96716 100644 --- a/sound/soc/qcom/apq8096.c +++ b/sound/soc/qcom/apq8096.c @@ -31,8 +31,8 @@ static int msm_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; u32 rx_ch_cnt = 0, tx_ch_cnt = 0; int ret = 0; @@ -66,7 +66,7 @@ static struct snd_soc_ops apq8096_ops = { static int apq8096_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); /* * Codec SLIMBUS configuration diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index b05091c283b7..34f7fd1bab1c 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -55,7 +55,7 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct lpass_variant *v = drvdata->variant; int ret, dma_ch, dir = substream->stream; @@ -529,7 +529,7 @@ static void lpass_platform_pcm_free(struct snd_soc_component *component, struct snd_pcm_substream *substream; int i; - for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + for_each_pcm_streams(i) { substream = pcm->streams[i].substream; if (substream) { snd_dma_free_pages(&substream->dma_buffer); diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index c0d422d0ab94..f6c7cddf08e8 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -41,6 +41,9 @@ #define Q6ASM_DAI_TX 1 #define Q6ASM_DAI_RX 2 +#define ALAC_CH_LAYOUT_MONO ((101 << 16) | 1) +#define ALAC_CH_LAYOUT_STEREO ((101 << 16) | 2) + enum stream_state { Q6ASM_STREAM_IDLE = 0, Q6ASM_STREAM_STOPPED, @@ -69,6 +72,8 @@ struct q6asm_dai_rtd { }; struct q6asm_dai_data { + struct snd_soc_dai_driver *dais; + int num_dais; long long int sid; }; @@ -250,7 +255,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM, - prtd->bits_per_sample); + 0, prtd->bits_per_sample); } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM, prtd->bits_per_sample); @@ -328,7 +333,7 @@ static int q6asm_dai_open(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; - struct snd_soc_dai *cpu_dai = soc_prtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_prtd, 0); struct q6asm_dai_rtd *prtd; struct q6asm_dai_data *pdata; struct device *dev = component->dev; @@ -540,7 +545,7 @@ static int q6asm_dai_compr_open(struct snd_compr_stream *stream) struct snd_soc_pcm_runtime *rtd = stream->private_data; struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_compr_runtime *runtime = stream->runtime; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct q6asm_dai_data *pdata; struct device *dev = c->dev; struct q6asm_dai_rtd *prtd; @@ -627,10 +632,17 @@ static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream, int dir = stream->direction; struct q6asm_dai_data *pdata; struct q6asm_flac_cfg flac_cfg; + struct q6asm_wma_cfg wma_cfg; + struct q6asm_alac_cfg alac_cfg; + struct q6asm_ape_cfg ape_cfg; + unsigned int wma_v9 = 0; struct device *dev = c->dev; int ret; union snd_codec_options *codec_options; struct snd_dec_flac *flac; + struct snd_dec_wma *wma; + struct snd_dec_alac *alac; + struct snd_dec_ape *ape; codec_options = &(prtd->codec_param.codec.options); @@ -652,7 +664,7 @@ static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream, prtd->bits_per_sample = 16; if (dir == SND_COMPRESS_PLAYBACK) { ret = q6asm_open_write(prtd->audio_client, params->codec.id, - prtd->bits_per_sample); + params->codec.profile, prtd->bits_per_sample); if (ret < 0) { dev_err(dev, "q6asm_open_write failed\n"); @@ -692,6 +704,126 @@ static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream, return -EIO; } break; + + case SND_AUDIOCODEC_WMA: + wma = &codec_options->wma_d; + + memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg)); + + wma_cfg.sample_rate = params->codec.sample_rate; + wma_cfg.num_channels = params->codec.ch_in; + wma_cfg.bytes_per_sec = params->codec.bit_rate / 8; + wma_cfg.block_align = params->codec.align; + wma_cfg.bits_per_sample = prtd->bits_per_sample; + wma_cfg.enc_options = wma->encoder_option; + wma_cfg.adv_enc_options = wma->adv_encoder_option; + wma_cfg.adv_enc_options2 = wma->adv_encoder_option2; + + if (wma_cfg.num_channels == 1) + wma_cfg.channel_mask = 4; /* Mono Center */ + else if (wma_cfg.num_channels == 2) + wma_cfg.channel_mask = 3; /* Stereo FL/FR */ + else + return -EINVAL; + + /* check the codec profile */ + switch (params->codec.profile) { + case SND_AUDIOPROFILE_WMA9: + wma_cfg.fmtag = 0x161; + wma_v9 = 1; + break; + + case SND_AUDIOPROFILE_WMA10: + wma_cfg.fmtag = 0x166; + break; + + case SND_AUDIOPROFILE_WMA9_PRO: + wma_cfg.fmtag = 0x162; + break; + + case SND_AUDIOPROFILE_WMA9_LOSSLESS: + wma_cfg.fmtag = 0x163; + break; + + case SND_AUDIOPROFILE_WMA10_LOSSLESS: + wma_cfg.fmtag = 0x167; + break; + + default: + dev_err(dev, "Unknown WMA profile:%x\n", + params->codec.profile); + return -EIO; + } + + if (wma_v9) + ret = q6asm_stream_media_format_block_wma_v9( + prtd->audio_client, &wma_cfg); + else + ret = q6asm_stream_media_format_block_wma_v10( + prtd->audio_client, &wma_cfg); + if (ret < 0) { + dev_err(dev, "WMA9 CMD failed:%d\n", ret); + return -EIO; + } + break; + + case SND_AUDIOCODEC_ALAC: + memset(&alac_cfg, 0x0, sizeof(alac_cfg)); + alac = &codec_options->alac_d; + + alac_cfg.sample_rate = params->codec.sample_rate; + alac_cfg.avg_bit_rate = params->codec.bit_rate; + alac_cfg.bit_depth = prtd->bits_per_sample; + alac_cfg.num_channels = params->codec.ch_in; + + alac_cfg.frame_length = alac->frame_length; + alac_cfg.pb = alac->pb; + alac_cfg.mb = alac->mb; + alac_cfg.kb = alac->kb; + alac_cfg.max_run = alac->max_run; + alac_cfg.compatible_version = alac->compatible_version; + alac_cfg.max_frame_bytes = alac->max_frame_bytes; + + switch (params->codec.ch_in) { + case 1: + alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO; + break; + case 2: + alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_STEREO; + break; + } + ret = q6asm_stream_media_format_block_alac(prtd->audio_client, + &alac_cfg); + if (ret < 0) { + dev_err(dev, "ALAC CMD Format block failed:%d\n", ret); + return -EIO; + } + break; + + case SND_AUDIOCODEC_APE: + memset(&ape_cfg, 0x0, sizeof(ape_cfg)); + ape = &codec_options->ape_d; + + ape_cfg.sample_rate = params->codec.sample_rate; + ape_cfg.num_channels = params->codec.ch_in; + ape_cfg.bits_per_sample = prtd->bits_per_sample; + + ape_cfg.compatible_version = ape->compatible_version; + ape_cfg.compression_level = ape->compression_level; + ape_cfg.format_flags = ape->format_flags; + ape_cfg.blocks_per_frame = ape->blocks_per_frame; + ape_cfg.final_frame_blocks = ape->final_frame_blocks; + ape_cfg.total_frames = ape->total_frames; + ape_cfg.seek_table_present = ape->seek_table_present; + + ret = q6asm_stream_media_format_block_ape(prtd->audio_client, + &ape_cfg); + if (ret < 0) { + dev_err(dev, "APE CMD Format block failed:%d\n", ret); + return -EIO; + } + break; + default: break; } @@ -791,9 +923,12 @@ static int q6asm_dai_compr_get_caps(struct snd_compr_stream *stream, caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; - caps->num_codecs = 2; + caps->num_codecs = 5; caps->codecs[0] = SND_AUDIOCODEC_MP3; caps->codecs[1] = SND_AUDIOCODEC_FLAC; + caps->codecs[2] = SND_AUDIOCODEC_WMA; + caps->codecs[3] = SND_AUDIOCODEC_ALAC; + caps->codecs[4] = SND_AUDIOCODEC_APE; return 0; } @@ -889,7 +1024,7 @@ static const struct snd_soc_component_driver q6asm_fe_dai_component = { .compr_ops = &q6asm_dai_compr_ops, }; -static struct snd_soc_dai_driver q6asm_fe_dais[] = { +static struct snd_soc_dai_driver q6asm_fe_dais_template[] = { Q6ASM_FEDAI_DRIVER(1), Q6ASM_FEDAI_DRIVER(2), Q6ASM_FEDAI_DRIVER(3), @@ -903,10 +1038,22 @@ static struct snd_soc_dai_driver q6asm_fe_dais[] = { static int of_q6asm_parse_dai_data(struct device *dev, struct q6asm_dai_data *pdata) { - static struct snd_soc_dai_driver *dai_drv; + struct snd_soc_dai_driver *dai_drv; struct snd_soc_pcm_stream empty_stream; struct device_node *node; - int ret, id, dir; + int ret, id, dir, idx = 0; + + + pdata->num_dais = of_get_child_count(dev->of_node); + if (!pdata->num_dais) { + dev_err(dev, "No dais found in DT\n"); + return -EINVAL; + } + + pdata->dais = devm_kcalloc(dev, pdata->num_dais, sizeof(*dai_drv), + GFP_KERNEL); + if (!pdata->dais) + return -ENOMEM; memset(&empty_stream, 0, sizeof(empty_stream)); @@ -917,7 +1064,8 @@ static int of_q6asm_parse_dai_data(struct device *dev, continue; } - dai_drv = &q6asm_fe_dais[id]; + dai_drv = &pdata->dais[idx++]; + *dai_drv = q6asm_fe_dais_template[id]; ret = of_property_read_u32(node, "direction", &dir); if (ret) @@ -955,11 +1103,12 @@ static int q6asm_dai_probe(struct platform_device *pdev) dev_set_drvdata(dev, pdata); - of_q6asm_parse_dai_data(dev, pdata); + rc = of_q6asm_parse_dai_data(dev, pdata); + if (rc) + return rc; return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component, - q6asm_fe_dais, - ARRAY_SIZE(q6asm_fe_dais)); + pdata->dais, pdata->num_dais); } static const struct of_device_id q6asm_dai_device_id[] = { diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 36e0eab13a98..0e0e8f7a460a 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -39,6 +39,8 @@ #define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 #define ASM_MEDIA_FMT_MP3 0x00010BE9 #define ASM_MEDIA_FMT_FLAC 0x00010C16 +#define ASM_MEDIA_FMT_WMA_V9 0x00010DA8 +#define ASM_MEDIA_FMT_WMA_V10 0x00010DA7 #define ASM_DATA_CMD_WRITE_V2 0x00010DAB #define ASM_DATA_CMD_READ_V2 0x00010DAC #define ASM_SESSION_CMD_SUSPEND 0x00010DEC @@ -46,6 +48,8 @@ #define ASM_STREAM_CMD_OPEN_READ_V3 0x00010DB4 #define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A #define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D +#define ASM_MEDIA_FMT_ALAC 0x00012f31 +#define ASM_MEDIA_FMT_APE 0x00012f32 #define ASM_LEGACY_STREAM_SESSION 0 @@ -104,6 +108,63 @@ struct asm_flac_fmt_blk_v2 { u16 reserved; } __packed; +struct asm_wmastdv9_fmt_blk_v2 { + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + u16 fmtag; + u16 num_channels; + u32 sample_rate; + u32 bytes_per_sec; + u16 blk_align; + u16 bits_per_sample; + u32 channel_mask; + u16 enc_options; + u16 reserved; +} __packed; + +struct asm_wmaprov10_fmt_blk_v2 { + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + u16 fmtag; + u16 num_channels; + u32 sample_rate; + u32 bytes_per_sec; + u16 blk_align; + u16 bits_per_sample; + u32 channel_mask; + u16 enc_options; + u16 advanced_enc_options1; + u32 advanced_enc_options2; +} __packed; + +struct asm_alac_fmt_blk_v2 { + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + u32 frame_length; + u8 compatible_version; + u8 bit_depth; + u8 pb; + u8 mb; + u8 kb; + u8 num_channels; + u16 max_run; + u32 max_frame_bytes; + u32 avg_bit_rate; + u32 sample_rate; + u32 channel_layout_tag; +} __packed; + +struct asm_ape_fmt_blk_v2 { + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + u16 compatible_version; + u16 compression_level; + u32 format_flags; + u32 blocks_per_frame; + u32 final_frame_blocks; + u32 total_frames; + u16 bits_per_sample; + u16 num_channels; + u32 sample_rate; + u32 seek_table_present; +} __packed; + struct asm_stream_cmd_set_encdec_param { u32 param_id; u32 param_size; @@ -858,7 +919,7 @@ err: * Return: Will be an negative value on error or zero on success */ int q6asm_open_write(struct audio_client *ac, uint32_t format, - uint16_t bits_per_sample) + u32 codec_profile, uint16_t bits_per_sample) { struct asm_stream_cmd_open_write_v3 *open; struct apr_pkt *pkt; @@ -894,6 +955,30 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format, case SND_AUDIOCODEC_FLAC: open->dec_fmt_id = ASM_MEDIA_FMT_FLAC; break; + case SND_AUDIOCODEC_WMA: + switch (codec_profile) { + case SND_AUDIOPROFILE_WMA9: + open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V9; + break; + case SND_AUDIOPROFILE_WMA10: + case SND_AUDIOPROFILE_WMA9_PRO: + case SND_AUDIOPROFILE_WMA9_LOSSLESS: + case SND_AUDIOPROFILE_WMA10_LOSSLESS: + open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V10; + break; + default: + dev_err(ac->dev, "Invalid codec profile 0x%x\n", + codec_profile); + rc = -EINVAL; + goto err; + } + break; + case SND_AUDIOCODEC_ALAC: + open->dec_fmt_id = ASM_MEDIA_FMT_ALAC; + break; + case SND_AUDIOCODEC_APE: + open->dec_fmt_id = ASM_MEDIA_FMT_APE; + break; default: dev_err(ac->dev, "Invalid format 0x%x\n", format); rc = -EINVAL; @@ -1075,6 +1160,162 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac, return rc; } EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac); + +int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, + struct q6asm_wma_cfg *cfg) +{ + struct asm_wmastdv9_fmt_blk_v2 *fmt; + struct apr_pkt *pkt; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(*fmt); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + fmt = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + + pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); + fmt->fmtag = cfg->fmtag; + fmt->num_channels = cfg->num_channels; + fmt->sample_rate = cfg->sample_rate; + fmt->bytes_per_sec = cfg->bytes_per_sec; + fmt->blk_align = cfg->block_align; + fmt->bits_per_sample = cfg->bits_per_sample; + fmt->channel_mask = cfg->channel_mask; + fmt->enc_options = cfg->enc_options; + fmt->reserved = 0; + + rc = q6asm_ac_send_cmd_sync(ac, pkt); + kfree(pkt); + + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v9); + +int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, + struct q6asm_wma_cfg *cfg) +{ + struct asm_wmaprov10_fmt_blk_v2 *fmt; + struct apr_pkt *pkt; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(*fmt); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + fmt = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + + pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); + fmt->fmtag = cfg->fmtag; + fmt->num_channels = cfg->num_channels; + fmt->sample_rate = cfg->sample_rate; + fmt->bytes_per_sec = cfg->bytes_per_sec; + fmt->blk_align = cfg->block_align; + fmt->bits_per_sample = cfg->bits_per_sample; + fmt->channel_mask = cfg->channel_mask; + fmt->enc_options = cfg->enc_options; + fmt->advanced_enc_options1 = cfg->adv_enc_options; + fmt->advanced_enc_options2 = cfg->adv_enc_options2; + + rc = q6asm_ac_send_cmd_sync(ac, pkt); + kfree(pkt); + + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v10); + +int q6asm_stream_media_format_block_alac(struct audio_client *ac, + struct q6asm_alac_cfg *cfg) +{ + struct asm_alac_fmt_blk_v2 *fmt; + struct apr_pkt *pkt; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(*fmt); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + fmt = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + + pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); + + fmt->frame_length = cfg->frame_length; + fmt->compatible_version = cfg->compatible_version; + fmt->bit_depth = cfg->bit_depth; + fmt->num_channels = cfg->num_channels; + fmt->max_run = cfg->max_run; + fmt->max_frame_bytes = cfg->max_frame_bytes; + fmt->avg_bit_rate = cfg->avg_bit_rate; + fmt->sample_rate = cfg->sample_rate; + fmt->channel_layout_tag = cfg->channel_layout_tag; + fmt->pb = cfg->pb; + fmt->mb = cfg->mb; + fmt->kb = cfg->kb; + + rc = q6asm_ac_send_cmd_sync(ac, pkt); + kfree(pkt); + + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_alac); + +int q6asm_stream_media_format_block_ape(struct audio_client *ac, + struct q6asm_ape_cfg *cfg) +{ + struct asm_ape_fmt_blk_v2 *fmt; + struct apr_pkt *pkt; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(*fmt); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + fmt = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + + pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); + + fmt->compatible_version = cfg->compatible_version; + fmt->compression_level = cfg->compression_level; + fmt->format_flags = cfg->format_flags; + fmt->blocks_per_frame = cfg->blocks_per_frame; + fmt->final_frame_blocks = cfg->final_frame_blocks; + fmt->total_frames = cfg->total_frames; + fmt->bits_per_sample = cfg->bits_per_sample; + fmt->num_channels = cfg->num_channels; + fmt->sample_rate = cfg->sample_rate; + fmt->seek_table_present = cfg->seek_table_present; + + rc = q6asm_ac_send_cmd_sync(ac, pkt); + kfree(pkt); + + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape); + /** * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture * diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index 6764f55f7078..38a207d6cd95 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -45,6 +45,47 @@ struct q6asm_flac_cfg { u16 md5_sum; }; +struct q6asm_wma_cfg { + u32 fmtag; + u32 num_channels; + u32 sample_rate; + u32 bytes_per_sec; + u32 block_align; + u32 bits_per_sample; + u32 channel_mask; + u32 enc_options; + u32 adv_enc_options; + u32 adv_enc_options2; +}; + +struct q6asm_alac_cfg { + u32 frame_length; + u8 compatible_version; + u8 bit_depth; + u8 pb; + u8 mb; + u8 kb; + u8 num_channels; + u16 max_run; + u32 max_frame_bytes; + u32 avg_bit_rate; + u32 sample_rate; + u32 channel_layout_tag; +}; + +struct q6asm_ape_cfg { + u16 compatible_version; + u16 compression_level; + u32 format_flags; + u32 blocks_per_frame; + u32 final_frame_blocks; + u32 total_frames; + u16 bits_per_sample; + u16 num_channels; + u32 sample_rate; + u32 seek_table_present; +}; + typedef void (*q6asm_cb) (uint32_t opcode, uint32_t token, void *payload, void *priv); struct audio_client; @@ -55,7 +96,7 @@ void q6asm_audio_client_free(struct audio_client *ac); int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t flags); int q6asm_open_write(struct audio_client *ac, uint32_t format, - uint16_t bits_per_sample); + u32 codec_profile, uint16_t bits_per_sample); int q6asm_open_read(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample); @@ -69,6 +110,14 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, uint16_t bits_per_sample); int q6asm_stream_media_format_block_flac(struct audio_client *ac, struct q6asm_flac_cfg *cfg); +int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, + struct q6asm_wma_cfg *cfg); +int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, + struct q6asm_wma_cfg *cfg); +int q6asm_stream_media_format_block_alac(struct audio_client *ac, + struct q6asm_alac_cfg *cfg); +int q6asm_stream_media_format_block_ape(struct audio_client *ac, + struct q6asm_ape_cfg *cfg); int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts); int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index 20724102e85a..46e50612b92c 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -918,25 +918,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL6", NULL, "MultiMedia6 Mixer"}, {"MM_UL7", NULL, "MultiMedia7 Mixer"}, {"MM_UL8", NULL, "MultiMedia8 Mixer"}, - - {"MM_DL1", NULL, "MultiMedia1 Playback" }, - {"MM_DL2", NULL, "MultiMedia2 Playback" }, - {"MM_DL3", NULL, "MultiMedia3 Playback" }, - {"MM_DL4", NULL, "MultiMedia4 Playback" }, - {"MM_DL5", NULL, "MultiMedia5 Playback" }, - {"MM_DL6", NULL, "MultiMedia6 Playback" }, - {"MM_DL7", NULL, "MultiMedia7 Playback" }, - {"MM_DL8", NULL, "MultiMedia8 Playback" }, - - {"MultiMedia1 Capture", NULL, "MM_UL1"}, - {"MultiMedia2 Capture", NULL, "MM_UL2"}, - {"MultiMedia3 Capture", NULL, "MM_UL3"}, - {"MultiMedia4 Capture", NULL, "MM_UL4"}, - {"MultiMedia5 Capture", NULL, "MM_UL5"}, - {"MultiMedia6 Capture", NULL, "MM_UL6"}, - {"MultiMedia7 Capture", NULL, "MM_UL7"}, - {"MultiMedia8 Capture", NULL, "MM_UL8"}, - }; static int routing_hw_params(struct snd_soc_component *component, @@ -945,7 +926,7 @@ static int routing_hw_params(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct msm_routing_data *data = dev_get_drvdata(component->dev); - unsigned int be_id = rtd->cpu_dai->id; + unsigned int be_id = asoc_rtd_to_cpu(rtd, 0)->id; struct session_data *session; int path_type; diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 3b5547a27aad..b2de65c7f95c 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -11,6 +11,7 @@ #include <sound/pcm_params.h> #include <sound/jack.h> #include <sound/soc.h> +#include <linux/soundwire/sdw.h> #include <uapi/linux/input-event-codes.h> #include "common.h" #include "qdsp6/q6afe.h" @@ -31,10 +32,12 @@ struct sdm845_snd_data { struct snd_soc_jack jack; bool jack_setup; + bool stream_prepared[SLIM_MAX_RX_PORTS]; struct snd_soc_card *card; uint32_t pri_mi2s_clk_count; uint32_t sec_mi2s_clk_count; uint32_t quat_tdm_clk_count; + struct sdw_stream_runtime *sruntime[SLIM_MAX_RX_PORTS]; }; static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; @@ -43,14 +46,21 @@ static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai_link *dai_link = rtd->dai_link; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card); u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + struct sdw_stream_runtime *sruntime; u32 rx_ch_cnt = 0, tx_ch_cnt = 0; int ret = 0, i; - for (i = 0 ; i < dai_link->num_codecs; i++) { - ret = snd_soc_dai_get_channel_map(rtd->codec_dais[i], + for_each_rtd_codec_dais(rtd, i, codec_dai) { + sruntime = snd_soc_dai_get_sdw_stream(codec_dai, + substream->stream); + if (sruntime != ERR_PTR(-ENOTSUPP)) + pdata->sruntime[cpu_dai->id] = sruntime; + + ret = snd_soc_dai_get_channel_map(codec_dai, &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); if (ret != 0 && ret != -ENOTSUPP) { @@ -76,7 +86,8 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; int ret = 0, j; int channels, slot_width; @@ -125,8 +136,7 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, } } - for (j = 0; j < rtd->num_codecs; j++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + for_each_rtd_codec_dais(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name_prefix, "Left")) { ret = snd_soc_dai_set_tdm_slot( @@ -161,8 +171,8 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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 = 0; switch (cpu_dai->id) { @@ -210,11 +220,10 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component; struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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_jack *jack; - struct snd_soc_dai_link *dai_link = rtd->dai_link; /* * Codec SLIMBUS configuration * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 @@ -266,8 +275,8 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) } break; case SLIMBUS_0_RX...SLIMBUS_6_TX: - for (i = 0 ; i < dai_link->num_codecs; i++) { - rval = snd_soc_dai_set_channel_map(rtd->codec_dais[i], + for_each_rtd_codec_dais(rtd, i, codec_dai) { + rval = snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), tx_ch, ARRAY_SIZE(rx_ch), @@ -275,7 +284,7 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) if (rval != 0 && rval != -ENOTSUPP) return rval; - snd_soc_dai_set_sysclk(rtd->codec_dais[i], 0, + snd_soc_dai_set_sysclk(codec_dai, 0, WCD934X_DEFAULT_MCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); } @@ -295,8 +304,8 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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 j; int ret; @@ -345,8 +354,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) codec_dai_fmt |= SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B; - for (j = 0; j < rtd->num_codecs; j++) { - codec_dai = rtd->codec_dais[j]; + for_each_rtd_codec_dais(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name_prefix, "Left")) { @@ -386,7 +394,7 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); switch (cpu_dai->id) { case PRIMARY_MI2S_RX: @@ -427,8 +435,65 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) } } +static int sdm845_snd_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdm845_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + int ret; + + if (!sruntime) + return 0; + + if (data->stream_prepared[cpu_dai->id]) { + sdw_disable_stream(sruntime); + sdw_deprepare_stream(sruntime); + data->stream_prepared[cpu_dai->id] = false; + } + + ret = sdw_prepare_stream(sruntime); + if (ret) + return ret; + + /** + * NOTE: there is a strict hw requirement about the ordering of port + * enables and actual WSA881x PA enable. PA enable should only happen + * after soundwire ports are enabled if not DC on the line is + * accumulated resulting in Click/Pop Noise + * PA enable/mute are handled as part of codec DAPM and digital mute. + */ + + ret = sdw_enable_stream(sruntime); + if (ret) { + sdw_deprepare_stream(sruntime); + return ret; + } + data->stream_prepared[cpu_dai->id] = true; + + return ret; +} + +static int sdm845_snd_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdm845_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + + if (sruntime && data->stream_prepared[cpu_dai->id]) { + sdw_disable_stream(sruntime); + sdw_deprepare_stream(sruntime); + data->stream_prepared[cpu_dai->id] = false; + } + + return 0; +} + static const struct snd_soc_ops sdm845_be_ops = { .hw_params = sdm845_snd_hw_params, + .hw_free = sdm845_snd_hw_free, + .prepare = sdm845_snd_prepare, .startup = sdm845_snd_startup, .shutdown = sdm845_snd_shutdown, }; diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c index e6666e597265..3a6e18709b9e 100644 --- a/sound/soc/qcom/storm.c +++ b/sound/soc/qcom/storm.c @@ -39,7 +39,7 @@ static int storm_ops_hw_params(struct snd_pcm_substream *substream, */ sysclk_freq = rate * bitwidth * 2 * STORM_SYSCLK_MULT; - ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0); + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(soc_runtime, 0), 0, sysclk_freq, 0); if (ret) { dev_err(card->dev, "error setting sysclk to %u: %d\n", sysclk_freq, ret); diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c index 767700c34ee2..01078155a914 100644 --- a/sound/soc/rockchip/rk3288_hdmi_analog.c +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -67,8 +67,8 @@ static int rk_hw_params(struct snd_pcm_substream *substream, { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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 mclk; switch (params_rate(params)) { diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index d951100bf770..f45e5aaa4b30 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -57,7 +57,7 @@ static int rockchip_sound_max98357a_hw_params(struct snd_pcm_substream *substrea mclk = params_rate(params) * SOUND_FS; - ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, 0); + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk, 0); if (ret) { dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n", __func__, mclk, ret); @@ -71,8 +71,8 @@ static int rockchip_sound_rt5514_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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 mclk; int ret; @@ -103,8 +103,8 @@ static int rockchip_sound_da7219_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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 mclk, ret; /* in bypass mode, the mclk has to be one of the frequencies below */ @@ -153,8 +153,8 @@ static int rockchip_sound_da7219_hw_params(struct snd_pcm_substream *substream, static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dais[0]->component; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; /* We need default MCLK and PLL settings for the accessory detection */ @@ -206,7 +206,7 @@ static int rockchip_sound_dmic_hw_params(struct snd_pcm_substream *substream, mclk = params_rate(params) * SOUND_FS; - ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, 0); + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk, 0); if (ret) { dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n", __func__, mclk, ret); diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 60930fa85aa4..1f527d3763ce 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -146,8 +146,8 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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 mclk; switch (params_rate(params)) { @@ -227,7 +227,7 @@ static struct snd_soc_jack rk_hdmi_jack; static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; - struct snd_soc_component *component = runtime->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; int ret; /* enable jack detection */ diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index 26b67b245484..0617ccf4e42c 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -56,8 +56,8 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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 mclk; switch (params_rate(params)) { @@ -113,7 +113,7 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime) return ret; } - return rt5645_set_jack_detect(runtime->codec_dai->component, + return rt5645_set_jack_detect(asoc_rtd_to_codec(runtime, 0)->component, &headset_jack, &headset_jack, &headset_jack); diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 1a0b163ca47b..112911dc271b 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -151,7 +151,7 @@ config SND_SOC_TOBERMORY config SND_SOC_BELLS tristate "Audio support for Wolfson Bells" - depends on MFD_ARIZONA && I2C && SPI_MASTER + depends on MFD_ARIZONA && MFD_WM5102 && MFD_WM5110 && I2C && SPI_MASTER depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select SND_SOC_WM5102 @@ -204,7 +204,7 @@ config SND_SOC_ARNDALE config SND_SOC_SAMSUNG_TM2_WM5110 tristate "SoC I2S Audio support for WM5110 on TM2 board" - depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER + depends on SND_SOC_SAMSUNG && MFD_ARIZONA && MFD_WM5110 && I2C && SPI_MASTER depends on GPIOLIB || COMPILE_TEST select SND_SOC_MAX98504 select SND_SOC_WM5110 diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c index d64602950cbd..c81ece78e036 100644 --- a/sound/soc/samsung/arndale.c +++ b/sound/soc/samsung/arndale.c @@ -21,8 +21,8 @@ static int arndale_rt5631_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + 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 rfs, ret; unsigned long rclk; @@ -56,7 +56,7 @@ static int arndale_wm1811_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int rfs, rclk; /* Ensure AIF1CLK is >= 3 MHz for optimal performance */ @@ -174,7 +174,9 @@ static int arndale_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(card->dev, card); if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "snd_soc_register_card() failed: %d\n", ret); goto err_put_of_nodes; } return 0; diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index 5de633497f83..8b83f39c3ac9 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -60,7 +60,7 @@ static int bells_set_bias_level(struct snd_soc_card *card, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]); - codec_dai = rtd->codec_dai; + codec_dai = asoc_rtd_to_codec(rtd, 0); component = codec_dai->component; if (dapm->dev != codec_dai->dev) @@ -106,7 +106,7 @@ static int bells_set_bias_level_post(struct snd_soc_card *card, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]); - codec_dai = rtd->codec_dai; + codec_dai = asoc_rtd_to_codec(rtd, 0); component = codec_dai->component; if (dapm->dev != codec_dai->dev) @@ -152,11 +152,11 @@ static int bells_late_probe(struct snd_soc_card *card) int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_AP_DSP]); - wm0010 = rtd->codec_dai->component; + wm0010 = asoc_rtd_to_codec(rtd, 0)->component; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]); - component = rtd->codec_dai->component; - aif1_dai = rtd->codec_dai; + component = asoc_rtd_to_codec(rtd, 0)->component; + aif1_dai = asoc_rtd_to_codec(rtd, 0); ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK, ARIZONA_CLK_SRC_FLL1, @@ -195,7 +195,7 @@ static int bells_late_probe(struct snd_soc_card *card) } rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_CP]); - aif2_dai = rtd->cpu_dai; + aif2_dai = asoc_rtd_to_cpu(rtd, 0); ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); if (ret != 0) { @@ -207,8 +207,8 @@ static int bells_late_probe(struct snd_soc_card *card) return 0; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_SUB]); - aif3_dai = rtd->cpu_dai; - wm9081_dai = rtd->codec_dai; + aif3_dai = asoc_rtd_to_cpu(rtd, 0); + wm9081_dai = asoc_rtd_to_codec(rtd, 0); ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0); if (ret != 0) { diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index a95c34e53a2b..9139a1e7e200 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -68,7 +68,7 @@ static int h1940_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int div; int ret; unsigned int rate = params_rate(params); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index a57bb989a0ef..f86e3028b402 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -932,7 +932,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream, struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct i2s_dai *i2s = to_info(rtd->cpu_dai); + struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); unsigned long flags; switch (cmd) { diff --git a/sound/soc/samsung/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c index 949d2e029962..30899016cf08 100644 --- a/sound/soc/samsung/jive_wm8750.c +++ b/sound/soc/samsung/jive_wm8750.c @@ -33,8 +33,8 @@ static int jive_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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 s3c_i2sv2_rate_calc div; unsigned int clk = 0; int ret = 0; diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 59904f44118b..f4375c49f7f4 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -23,7 +23,7 @@ static int littlemill_set_bias_level(struct snd_soc_card *card, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - aif1_dai = rtd->codec_dai; + aif1_dai = asoc_rtd_to_codec(rtd, 0); if (dapm->dev != aif1_dai->dev) return 0; @@ -70,7 +70,7 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - aif1_dai = rtd->codec_dai; + aif1_dai = asoc_rtd_to_codec(rtd, 0); if (dapm->dev != aif1_dai->dev) return 0; @@ -105,7 +105,7 @@ static int littlemill_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; sample_rate = params_rate(params); @@ -181,7 +181,7 @@ static int bbclk_ev(struct snd_soc_dapm_widget *w, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); - aif2_dai = rtd->cpu_dai; + aif2_dai = asoc_rtd_to_cpu(rtd, 0); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -264,11 +264,11 @@ static int littlemill_late_probe(struct snd_soc_card *card) int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - component = rtd->codec_dai->component; - aif1_dai = rtd->codec_dai; + component = asoc_rtd_to_codec(rtd, 0)->component; + aif1_dai = asoc_rtd_to_codec(rtd, 0); rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); - aif2_dai = rtd->cpu_dai; + aif2_dai = asoc_rtd_to_cpu(rtd, 0); ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, 32768, SND_SOC_CLOCK_IN); @@ -325,7 +325,7 @@ static int littlemill_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) + if (ret && ret != -EPROBE_DEFER) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 098eefc764db..998d10cf8c94 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -32,7 +32,7 @@ static struct snd_soc_jack_pin lowland_headset_pins[] = { static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret; ret = snd_soc_component_set_sysclk(component, WM5100_CLK_SYSCLK, @@ -65,7 +65,7 @@ static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; snd_soc_dapm_nc_pin(&rtd->card->dapm, "LINEOUT"); @@ -183,7 +183,7 @@ static int lowland_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) + if (ret && ret != -EPROBE_DEFER) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index 1339e41e9860..b7ce1da854ce 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -26,8 +26,8 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int pll_out = 0, bclk = 0; int ret = 0; unsigned long iis_clkrate; @@ -100,7 +100,7 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); /* disable the PLL */ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); @@ -118,7 +118,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int pcmdiv = 0; int ret = 0; unsigned long iis_clkrate; @@ -155,7 +155,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); /* disable the PLL */ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index f0f5fa9c27d3..6eda5af989fe 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -98,7 +98,7 @@ static int odroid_card_be_hw_params(struct snd_pcm_substream *substream, return ret; if (rtd->num_codecs > 1) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[1]; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 1); ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk_freq, SND_SOC_CLOCK_IN); @@ -311,7 +311,9 @@ static int odroid_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { - dev_err(dev, "snd_soc_register_card() failed: %d\n", ret); + if (ret != -EPROBE_DEFER) + dev_err(dev, "snd_soc_register_card() failed: %d\n", + ret); goto err_put_clk_i2s; } diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index f6e67d0e7882..a5b1a12b3496 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -212,7 +212,7 @@ static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); unsigned long flags; dev_dbg(pcm->dev, "Entered %s\n", __func__); @@ -256,7 +256,7 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *socdai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); void __iomem *regs = pcm->regs; struct clk *clk; int sclk_div, sync_div; diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 4b247e91ae5b..3afe63c0923e 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -149,7 +149,7 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int div; int ret; unsigned int rate = params_rate(params); diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index 593be1b668d6..358887848293 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -380,7 +380,7 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct s3c_i2sv2_info *i2s = to_info(rtd->cpu_dai); + struct s3c_i2sv2_info *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); unsigned long irqs; int ret = 0; diff --git a/sound/soc/samsung/s3c24xx_simtec.c b/sound/soc/samsung/s3c24xx_simtec.c index 4543705b8d87..fd2a4da086f3 100644 --- a/sound/soc/samsung/s3c24xx_simtec.c +++ b/sound/soc/samsung/s3c24xx_simtec.c @@ -160,8 +160,8 @@ static int simtec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(codec_dai, 0, diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index 55d2a802a6cb..abb5c4713c53 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -51,7 +51,7 @@ static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret = 0; mutex_lock(&priv->clk_lock); @@ -119,8 +119,8 @@ static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int clk = 0; int ret = 0; int clk_source, fs_mode; diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index fab3db9fdb98..36bef136d57f 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -25,8 +25,8 @@ static int smartq_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int clk = 0; int ret; diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c index 4baef84d29ee..776a270261bf 100644 --- a/sound/soc/samsung/smdk_spdif.c +++ b/sound/soc/samsung/smdk_spdif.c @@ -101,7 +101,7 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned long pll_out, rclk_rate; int ret, ratio; diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index d096ff912260..02074c34a2b2 100644 --- a/sound/soc/samsung/smdk_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -23,7 +23,7 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int pll_out; int rfs, ret; diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index 28f8be000aa1..a9f345f19a8a 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -45,7 +45,7 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int pll_out; int ret; @@ -178,7 +178,7 @@ static int smdk_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) + if (ret && ret != -EPROBE_DEFER) dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); return ret; diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c index 2e3dc7320c62..746930dde5d7 100644 --- a/sound/soc/samsung/smdk_wm8994pcm.c +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -44,8 +44,8 @@ static int smdk_wm8994_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned long mclk_freq; int rfs, ret; @@ -118,7 +118,7 @@ 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) + if (ret && ret != -EPROBE_DEFER) dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); return ret; diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index f075aae9561a..40c5de8df0ff 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -110,9 +110,9 @@ static int snow_late_probe(struct snd_soc_card *card) /* In the multi-codec case codec_dais 0 is MAX98095 and 1 is HDMI. */ if (rtd->num_codecs > 1) - codec_dai = rtd->codec_dais[0]; + codec_dai = asoc_rtd_to_codec(rtd, 0); else - codec_dai = rtd->codec_dai; + codec_dai = asoc_rtd_to_codec(rtd, 0); /* Set the MCLK rate for the codec */ return snd_soc_dai_set_sysclk(codec_dai, 0, @@ -216,7 +216,9 @@ static int snow_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(dev, card); if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "snd_soc_register_card failed (%d)\n", ret); return ret; } diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 1a9f08a50394..759fc6644329 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -142,7 +142,7 @@ static int spdif_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai); + struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); unsigned long flags; dev_dbg(spdif->dev, "Entered %s\n", __func__); @@ -178,7 +178,7 @@ static int spdif_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *socdai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai); + struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); void __iomem *regs = spdif->regs; struct snd_dmaengine_dai_dma_data *dma_data; u32 con, clkcon, cstas; @@ -194,7 +194,7 @@ static int spdif_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data); + snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_data); spin_lock_irqsave(&spdif->lock, flags); @@ -280,7 +280,7 @@ static void spdif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai); + struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); void __iomem *regs = spdif->regs; u32 con, clkcon; diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index ea0d1ec67f01..f5f6ba00d073 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -25,7 +25,7 @@ static int speyside_set_bias_level(struct snd_soc_card *card, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); - codec_dai = rtd->codec_dai; + codec_dai = asoc_rtd_to_codec(rtd, 0); if (dapm->dev != codec_dai->dev) return 0; @@ -61,7 +61,7 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); - codec_dai = rtd->codec_dai; + codec_dai = asoc_rtd_to_codec(rtd, 0); if (dapm->dev != codec_dai->dev) return 0; @@ -131,7 +131,7 @@ static void speyside_set_polarity(struct snd_soc_component *component, static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); int ret; ret = snd_soc_dai_set_sysclk(dai, 0, MCLK_AUDIO_RATE, 0); @@ -143,7 +143,7 @@ static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd) static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = dai->component; int ret; @@ -330,7 +330,7 @@ static int speyside_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) + if (ret && ret != -EPROBE_DEFER) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index 10ff14b856f2..6dfd540e2d74 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -93,7 +93,7 @@ static int tm2_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(rtd->card); switch (params_rate(params)) { @@ -134,7 +134,7 @@ static int tm2_aif2_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; unsigned int asyncclk_rate; int ret; @@ -188,7 +188,7 @@ static int tm2_aif2_hw_params(struct snd_pcm_substream *substream, static int tm2_aif2_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret; /* disable FLL2 */ @@ -209,7 +209,7 @@ static int tm2_hdmi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int bfs; int bitwidth, ret; @@ -284,7 +284,7 @@ static int tm2_set_bias_level(struct snd_soc_card *card, rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - if (dapm->dev != rtd->codec_dai->dev) + if (dapm->dev != asoc_rtd_to_codec(rtd, 0)->dev) return 0; switch (level) { @@ -315,8 +315,8 @@ static int tm2_late_probe(struct snd_soc_card *card) int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF1]); - aif1_dai = rtd->codec_dai; - priv->component = rtd->codec_dai->component; + aif1_dai = asoc_rtd_to_codec(rtd, 0); + priv->component = asoc_rtd_to_codec(rtd, 0)->component; ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0); if (ret < 0) { @@ -325,7 +325,7 @@ static int tm2_late_probe(struct snd_soc_card *card) } rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF2]); - aif2_dai = rtd->codec_dai; + aif2_dai = asoc_rtd_to_codec(rtd, 0); ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); if (ret < 0) { @@ -611,7 +611,8 @@ static int tm2_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { - dev_err(dev, "Failed to register card: %d\n", ret); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to register card: %d\n", ret); goto dai_node_put; } diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index fdce28cc26c4..c962d2c2a7f7 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -23,7 +23,7 @@ static int tobermory_set_bias_level(struct snd_soc_card *card, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - codec_dai = rtd->codec_dai; + codec_dai = asoc_rtd_to_codec(rtd, 0); if (dapm->dev != codec_dai->dev) return 0; @@ -66,7 +66,7 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card, int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - codec_dai = rtd->codec_dai; + codec_dai = asoc_rtd_to_codec(rtd, 0); if (dapm->dev != codec_dai->dev) return 0; @@ -181,8 +181,8 @@ static int tobermory_late_probe(struct snd_soc_card *card) int ret; rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - component = rtd->codec_dai->component; - codec_dai = rtd->codec_dai; + component = asoc_rtd_to_codec(rtd, 0)->component; + codec_dai = asoc_rtd_to_codec(rtd, 0); ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK, 32768, SND_SOC_CLOCK_IN); @@ -229,7 +229,7 @@ static int tobermory_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) + if (ret && ret != -EPROBE_DEFER) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index eee1a1e994cb..a35de78f14a9 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -119,7 +119,7 @@ static int camelot_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id]; + struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; int ret, dmairq; @@ -132,7 +132,7 @@ static int camelot_pcm_open(struct snd_soc_component *component, ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam); if (unlikely(ret)) { pr_debug("audio unit %d irqs already taken!\n", - rtd->cpu_dai->id); + asoc_rtd_to_cpu(rtd, 0)->id); return -EBUSY; } (void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam); @@ -141,7 +141,7 @@ static int camelot_pcm_open(struct snd_soc_component *component, ret = dmabrg_request_irq(dmairq, camelot_txdma, cam); if (unlikely(ret)) { pr_debug("audio unit %d irqs already taken!\n", - rtd->cpu_dai->id); + asoc_rtd_to_cpu(rtd, 0)->id); return -EBUSY; } (void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam); @@ -153,7 +153,7 @@ static int camelot_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id]; + struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; int dmairq; @@ -175,7 +175,7 @@ static int camelot_hw_params(struct snd_soc_component *component, struct snd_pcm_hw_params *hw_params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id]; + struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; int ret; @@ -194,7 +194,7 @@ static int camelot_prepare(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id]; + struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; pr_debug("PCM data: addr 0x%08lx len %d\n", (u32)runtime->dma_addr, runtime->dma_bytes); @@ -242,7 +242,7 @@ static int camelot_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id]; + struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; switch (cmd) { @@ -270,7 +270,7 @@ static snd_pcm_uframes_t camelot_pos(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id]; + struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; unsigned long pos; diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 4b35ef402604..1c3c4fdc9bef 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -408,7 +408,7 @@ static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - return rtd->cpu_dai; + return asoc_rtd_to_cpu(rtd, 0); } static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai) @@ -1938,8 +1938,7 @@ static int fsi_probe(struct platform_device *pdev) if (!master) return -ENOMEM; - master->base = devm_ioremap(&pdev->dev, - res->start, resource_size(res)); + master->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!master->base) { dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n"); return -ENXIO; diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c index 991557e25eba..d5702fbf176b 100644 --- a/sound/soc/sh/migor.c +++ b/sound/soc/sh/migor.c @@ -46,7 +46,7 @@ static int migor_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; unsigned int rate = params_rate(params); @@ -67,7 +67,7 @@ static int migor_hw_params(struct snd_pcm_substream *substream, clk_set_rate(&siumckb_clk, codec_freq); dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq); - ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, SIU_CLKB_EXT, + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), SIU_CLKB_EXT, codec_freq / 2, SND_SOC_CLOCK_IN); if (!ret) @@ -79,7 +79,7 @@ static int migor_hw_params(struct snd_pcm_substream *substream, static int migor_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); if (use_count) { use_count--; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 0bfcb77e5f65..4349f2fb823f 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -696,7 +696,7 @@ struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - return rtd->cpu_dai; + return asoc_rtd_to_cpu(rtd, 0); } static diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 392a1c5b15d3..50062eb79adb 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -810,9 +810,10 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) int playback = 0, capture = 0; int i; - if (rtd->num_codecs > 1) { + if (rtd->num_cpus > 1 || + rtd->num_codecs > 1) { dev_err(rtd->card->dev, - "Compress ASoC: Multicodec not supported\n"); + "Compress ASoC: Multi CPU/Codec not supported\n"); return -EINVAL; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 068d809c349a..843b8b1c89d4 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -365,19 +365,20 @@ EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *codec_dai = rtd->codec_dai; + int playback = SNDRV_PCM_STREAM_PLAYBACK; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", codec_dai->driver->playback.stream_name, - codec_dai->playback_active ? "active" : "inactive", + codec_dai->stream_active[playback] ? "active" : "inactive", rtd->pop_wait ? "yes" : "no"); /* are we waiting on this codec DAI stream */ if (rtd->pop_wait == 1) { rtd->pop_wait = 0; - snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + snd_soc_dapm_stream_event(rtd, playback, SND_SOC_DAPM_STREAM_STOP); } @@ -431,6 +432,7 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( struct snd_soc_component *component; struct device *dev; int ret; + int stream; /* * for rtd->dev @@ -465,23 +467,31 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( rtd->dev = dev; INIT_LIST_HEAD(&rtd->list); - INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients); - INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients); - INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients); - INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients); + for_each_pcm_streams(stream) { + INIT_LIST_HEAD(&rtd->dpcm[stream].be_clients); + INIT_LIST_HEAD(&rtd->dpcm[stream].fe_clients); + } dev_set_drvdata(dev, rtd); INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); /* - * for rtd->codec_dais + * for rtd->dais */ - rtd->codec_dais = devm_kcalloc(dev, dai_link->num_codecs, + rtd->dais = devm_kcalloc(dev, dai_link->num_cpus + dai_link->num_codecs, sizeof(struct snd_soc_dai *), GFP_KERNEL); - if (!rtd->codec_dais) + if (!rtd->dais) goto free_rtd; /* + * dais = [][][][][][][][][][][][][][][][][][] + * ^cpu_dais ^codec_dais + * |--- num_cpus ---|--- num_codecs --| + */ + rtd->cpu_dais = &rtd->dais[0]; + rtd->codec_dais = &rtd->dais[dai_link->num_cpus]; + + /* * rtd remaining settings */ rtd->card = card; @@ -514,6 +524,7 @@ int snd_soc_suspend(struct device *dev) struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_component *component; struct snd_soc_pcm_runtime *rtd; + int playback = SNDRV_PCM_STREAM_PLAYBACK; int i; /* If the card is not initialized yet there is nothing to do */ @@ -536,10 +547,9 @@ int snd_soc_suspend(struct device *dev) if (rtd->dai_link->ignore_suspend) continue; - for_each_rtd_codec_dai(rtd, i, dai) { - if (dai->playback_active) - snd_soc_dai_digital_mute(dai, 1, - SNDRV_PCM_STREAM_PLAYBACK); + for_each_rtd_codec_dais(rtd, i, dai) { + if (dai->stream_active[playback]) + snd_soc_dai_digital_mute(dai, 1, playback); } } @@ -558,17 +568,14 @@ int snd_soc_suspend(struct device *dev) snd_soc_flush_all_delayed_work(card); for_each_card_rtds(card, rtd) { + int stream; if (rtd->dai_link->ignore_suspend) continue; - snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_SUSPEND); - - snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_CAPTURE, - SND_SOC_DAPM_STREAM_SUSPEND); + for_each_pcm_streams(stream) + snd_soc_dapm_stream_event(rtd, stream, + SND_SOC_DAPM_STREAM_SUSPEND); } /* Recheck all endpoints too, their state is affected by suspend */ @@ -664,30 +671,27 @@ static void soc_resume_deferred(struct work_struct *work) } for_each_card_rtds(card, rtd) { + int stream; if (rtd->dai_link->ignore_suspend) continue; - snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_RESUME); - - snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_CAPTURE, - SND_SOC_DAPM_STREAM_RESUME); + for_each_pcm_streams(stream) + snd_soc_dapm_stream_event(rtd, stream, + SND_SOC_DAPM_STREAM_RESUME); } /* unmute any active DACs */ for_each_card_rtds(card, rtd) { struct snd_soc_dai *dai; + int playback = SNDRV_PCM_STREAM_PLAYBACK; if (rtd->dai_link->ignore_suspend) continue; - for_each_rtd_codec_dai(rtd, i, dai) { - if (dai->playback_active) - snd_soc_dai_digital_mute(dai, 0, - SNDRV_PCM_STREAM_PLAYBACK); + for_each_rtd_codec_dais(rtd, i, dai) { + if (dai->stream_active[playback]) + snd_soc_dai_digital_mute(dai, 0, playback); } } @@ -837,7 +841,7 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, struct snd_soc_dai_link *link) { int i; - struct snd_soc_dai_link_component *codec, *platform; + struct snd_soc_dai_link_component *cpu, *codec, *platform; for_each_link_codecs(link, i, codec) { /* @@ -886,44 +890,38 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, return -EPROBE_DEFER; } - /* FIXME */ - if (link->num_cpus > 1) { - dev_err(card->dev, - "ASoC: multi cpu is not yet supported %s\n", - link->name); - return -EINVAL; - } - - /* - * CPU device may be specified by either name or OF node, but - * can be left unspecified, and will be matched based on DAI - * name alone.. - */ - if (link->cpus->name && link->cpus->of_node) { - dev_err(card->dev, - "ASoC: Neither/both cpu name/of_node are set for %s\n", - link->name); - return -EINVAL; - } + for_each_link_cpus(link, i, cpu) { + /* + * CPU device may be specified by either name or OF node, but + * can be left unspecified, and will be matched based on DAI + * name alone.. + */ + if (cpu->name && cpu->of_node) { + dev_err(card->dev, + "ASoC: Neither/both cpu name/of_node are set for %s\n", + link->name); + return -EINVAL; + } - /* - * Defer card registration if cpu dai component is not added to - * component list. - */ - if ((link->cpus->of_node || link->cpus->name) && - !soc_find_component(link->cpus)) - return -EPROBE_DEFER; + /* + * Defer card registration if cpu dai component is not added to + * component list. + */ + if ((cpu->of_node || cpu->name) && + !soc_find_component(cpu)) + return -EPROBE_DEFER; - /* - * At least one of CPU DAI name or CPU device name/node must be - * specified - */ - if (!link->cpus->dai_name && - !(link->cpus->name || link->cpus->of_node)) { - dev_err(card->dev, - "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", - link->name); - return -EINVAL; + /* + * At least one of CPU DAI name or CPU device name/node must be + * specified + */ + if (!cpu->dai_name && + !(cpu->name || cpu->of_node)) { + dev_err(card->dev, + "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", + link->name); + return -EINVAL; + } } return 0; @@ -966,7 +964,7 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai_link_component *codec, *platform; + struct snd_soc_dai_link_component *codec, *platform, *cpu; struct snd_soc_component *component; int i, ret; @@ -991,14 +989,19 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card, if (!rtd) return -ENOMEM; - /* FIXME: we need multi CPU support in the future */ - rtd->cpu_dai = snd_soc_find_dai(dai_link->cpus); - if (!rtd->cpu_dai) { - dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", - dai_link->cpus->dai_name); - goto _err_defer; + rtd->num_cpus = dai_link->num_cpus; + for_each_link_cpus(dai_link, i, cpu) { + rtd->cpu_dais[i] = snd_soc_find_dai(cpu); + if (!rtd->cpu_dais[i]) { + dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", + cpu->dai_name); + goto _err_defer; + } + snd_soc_rtd_add_component(rtd, rtd->cpu_dais[i]->component); } - snd_soc_rtd_add_component(rtd, rtd->cpu_dai->component); + + /* Single cpu links expect cpu and cpu_dai in runtime data */ + rtd->cpu_dai = rtd->cpu_dais[0]; /* Find CODEC from registered CODECs */ rtd->num_codecs = dai_link->num_codecs; @@ -1034,20 +1037,20 @@ _err_defer: } EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtime); -static int soc_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, - struct snd_soc_pcm_runtime *rtd) +static int soc_dai_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_dai *dai; int i, ret = 0; - for (i = 0; i < num_dais; ++i) { - struct snd_soc_dai_driver *drv = dais[i]->driver; + for_each_rtd_dais(rtd, i, dai) { + struct snd_soc_dai_driver *drv = dai->driver; if (drv->pcm_new) - ret = drv->pcm_new(rtd, dais[i]); + ret = drv->pcm_new(rtd, dai); if (ret < 0) { - dev_err(dais[i]->dev, + dev_err(dai->dev, "ASoC: Failed to bind %s with pcm device\n", - dais[i]->name); + dai->name); return ret; } } @@ -1118,12 +1121,8 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card, dai_link->stream_name, ret); return ret; } - ret = soc_dai_pcm_new(&cpu_dai, 1, rtd); - if (ret < 0) - return ret; - ret = soc_dai_pcm_new(rtd->codec_dais, - rtd->num_codecs, rtd); - return ret; + + return soc_dai_pcm_new(rtd); } static void soc_set_name_prefix(struct snd_soc_card *card, @@ -1256,8 +1255,18 @@ static int soc_probe_component(struct snd_soc_card *card, ret = snd_soc_dapm_add_routes(dapm, component->driver->dapm_routes, component->driver->num_dapm_routes); - if (ret < 0) - goto err_probe; + if (ret < 0) { + if (card->disable_route_checks) { + dev_info(card->dev, + "%s: disable_route_checks set, ignoring errors on add_routes\n", + __func__); + } else { + dev_err(card->dev, + "%s: snd_soc_dapm_add_routes failed: %d\n", + __func__, ret); + goto err_probe; + } + } /* see for_each_card_components */ list_add(&component->card_list, &card->component_dev_list); @@ -1309,24 +1318,22 @@ static int soc_probe_dai(struct snd_soc_dai *dai, int order) static void soc_remove_link_dais(struct snd_soc_card *card) { int i; - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; struct snd_soc_pcm_runtime *rtd; int order; for_each_comp_order(order) { for_each_card_rtds(card, rtd) { - /* remove the CODEC DAI */ - for_each_rtd_codec_dai(rtd, i, codec_dai) - soc_remove_dai(codec_dai, order); - - soc_remove_dai(rtd->cpu_dai, order); + /* remove DAIs */ + for_each_rtd_dais(rtd, i, dai) + soc_remove_dai(dai, order); } } } static int soc_probe_link_dais(struct snd_soc_card *card) { - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; struct snd_soc_pcm_runtime *rtd; int i, order, ret; @@ -1337,13 +1344,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card) "ASoC: probe %s dai link %d late %d\n", card->name, rtd->num, order); - ret = soc_probe_dai(rtd->cpu_dai, order); - if (ret) - return ret; - - /* probe the CODEC DAI */ - for_each_rtd_codec_dai(rtd, i, codec_dai) { - ret = soc_probe_dai(codec_dai, order); + /* probe the CPU DAI */ + for_each_rtd_dais(rtd, i, dai) { + ret = soc_probe_dai(dai, order); if (ret) return ret; } @@ -1471,12 +1474,13 @@ static void soc_remove_aux_devices(struct snd_soc_card *card) int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, unsigned int dai_fmt) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; + unsigned int inv_dai_fmt; unsigned int i; int ret; - for_each_rtd_codec_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); if (ret != 0 && ret != -ENOTSUPP) { dev_warn(codec_dai->dev, @@ -1489,33 +1493,33 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, * Flip the polarity for the "CPU" end of a CODEC<->CODEC link * the component which has non_legacy_dai_naming is Codec */ - if (cpu_dai->component->driver->non_legacy_dai_naming) { - unsigned int inv_dai_fmt; - - inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; - switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; - break; - case SND_SOC_DAIFMT_CBM_CFS: - inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; - break; - case SND_SOC_DAIFMT_CBS_CFM: - inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; - break; - case SND_SOC_DAIFMT_CBS_CFS: - inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - break; - } - - dai_fmt = inv_dai_fmt; + inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; + switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + break; + case SND_SOC_DAIFMT_CBM_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + break; + case SND_SOC_DAIFMT_CBS_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + break; } + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + unsigned int fmt = dai_fmt; - ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); - if (ret != 0 && ret != -ENOTSUPP) { - dev_warn(cpu_dai->dev, - "ASoC: Failed to set DAI format: %d\n", ret); - return ret; + if (cpu_dai->component->driver->non_legacy_dai_naming) + fmt = inv_dai_fmt; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret != 0 && ret != -ENOTSUPP) { + dev_warn(cpu_dai->dev, + "ASoC: Failed to set DAI format: %d\n", ret); + return ret; + } } return 0; @@ -1938,8 +1942,18 @@ static int snd_soc_bind_card(struct snd_soc_card *card) ret = snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); - if (ret < 0) - goto probe_end; + if (ret < 0) { + if (card->disable_route_checks) { + dev_info(card->dev, + "%s: disable_route_checks set, ignoring errors on add_routes\n", + __func__); + } else { + dev_err(card->dev, + "%s: snd_soc_dapm_add_routes failed: %d\n", + __func__, ret); + goto probe_end; + } + } ret = snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, card->num_of_dapm_routes); @@ -3102,6 +3116,14 @@ int snd_soc_get_dai_name(struct of_phandle_args *args, *dai_name = dai->driver->name; if (!*dai_name) *dai_name = pos->name; + } else if (ret) { + /* + * if another error than ENOTSUPP is returned go on and + * check if another component is provided with the same + * node. This may happen if a device provides several + * components + */ + continue; } break; diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 51031e330179..19142f6e533c 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -295,17 +295,24 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai, { int ret = 0; - if (dai->driver->ops->startup) + if (!dai->started && + dai->driver->ops->startup) ret = dai->driver->ops->startup(substream, dai); + if (ret == 0) + dai->started = 1; + return ret; } void snd_soc_dai_shutdown(struct snd_soc_dai *dai, struct snd_pcm_substream *substream) { - if (dai->driver->ops->shutdown) + if (dai->started && + dai->driver->ops->shutdown) dai->driver->ops->shutdown(substream, dai); + + dai->started = 0; } int snd_soc_dai_prepare(struct snd_soc_dai *dai, @@ -383,12 +390,7 @@ int snd_soc_dai_compress_new(struct snd_soc_dai *dai, */ bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir) { - struct snd_soc_pcm_stream *stream; - - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - stream = &dai->driver->playback; - else - stream = &dai->driver->capture; + struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); /* If the codec specifies any channels at all, it supports the stream */ return stream->channels_min; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 9fb54e6fe254..04da7928c873 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -302,7 +302,7 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card) mutex_lock(&card->dapm_mutex); - list_for_each_entry(w, &card->widgets, list) { + for_each_card_widgets(card, w) { if (w->is_ep) { dapm_mark_dirty(w, "Rechecking endpoints"); if (w->is_ep & SND_SOC_DAPM_EP_SINK) @@ -589,7 +589,7 @@ static void dapm_reset(struct snd_soc_card *card) memset(&card->dapm_stats, 0, sizeof(card->dapm_stats)); - list_for_each_entry(w, &card->widgets, list) { + for_each_card_widgets(card, w) { w->new_power = w->power; w->power_checked = false; } @@ -833,7 +833,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, *kcontrol = NULL; - list_for_each_entry(w, &dapm->card->widgets, list) { + for_each_card_widgets(dapm->card, w) { if (w == kcontrolw || w->dapm != kcontrolw->dapm) continue; for (i = 0; i < w->num_kcontrols; i++) { @@ -1105,6 +1105,11 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) } } +static void dapm_widget_list_free(struct snd_soc_dapm_widget_list **list) +{ + kfree(*list); +} + static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, struct list_head *widgets) { @@ -1310,6 +1315,11 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, return paths; } +void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list) +{ + dapm_widget_list_free(list); +} + /* * Handler for regulator supply widget. */ @@ -1706,9 +1716,8 @@ static void dapm_seq_run(struct snd_soc_card *card, i, cur_subseq); } - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) soc_dapm_async_complete(d); - } } static void dapm_widget_update(struct snd_soc_card *card) @@ -1724,9 +1733,7 @@ static void dapm_widget_update(struct snd_soc_card *card) wlist = dapm_kcontrol_get_wlist(update->kcontrol); - for (wi = 0; wi < wlist->num_widgets; wi++) { - w = wlist->widgets[wi]; - + for_each_dapm_widgets(wlist, wi, w) { if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) { ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); if (ret != 0) @@ -1753,9 +1760,7 @@ static void dapm_widget_update(struct snd_soc_card *card) w->name, ret); } - for (wi = 0; wi < wlist->num_widgets; wi++) { - w = wlist->widgets[wi]; - + for_each_dapm_widgets(wlist, wi, w) { if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) { ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); if (ret != 0) @@ -1943,7 +1948,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) trace_snd_soc_dapm_start(card); - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) { if (dapm_idle_bias_off(d)) d->target_bias_level = SND_SOC_BIAS_OFF; else @@ -1962,7 +1967,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) dapm_power_one_widget(w, &up_list, &down_list); } - list_for_each_entry(w, &card->widgets, list) { + for_each_card_widgets(card, w) { switch (w->id) { case snd_soc_dapm_pre: case snd_soc_dapm_post: @@ -2007,10 +2012,10 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) * they're not ground referenced. */ bias = SND_SOC_BIAS_OFF; - list_for_each_entry(d, &card->dapm_list, list) + for_each_card_dapms(card, d) if (d->target_bias_level > bias) bias = d->target_bias_level; - list_for_each_entry(d, &card->dapm_list, list) + for_each_card_dapms(card, d) if (!dapm_idle_bias_off(d)) d->target_bias_level = bias; @@ -2019,7 +2024,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) /* Run card bias changes at first */ dapm_pre_sequence_async(&card->dapm, 0); /* Run other bias changes in parallel */ - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) { if (d != &card->dapm && d->bias_level != d->target_bias_level) async_schedule_domain(dapm_pre_sequence_async, d, &async_domain); @@ -2043,7 +2048,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) dapm_seq_run(card, &up_list, event, true); /* Run all the bias changes in parallel */ - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) { if (d != &card->dapm && d->bias_level != d->target_bias_level) async_schedule_domain(dapm_post_sequence_async, d, &async_domain); @@ -2053,7 +2058,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) dapm_post_sequence_async(&card->dapm, 0); /* do we need to notify any clients that DAPM event is complete */ - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) { if (!d->component) continue; @@ -2286,7 +2291,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); @@ -2351,7 +2356,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); @@ -2371,7 +2376,7 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, if (!cmpnt->card) return 0; - list_for_each_entry(w, &cmpnt->card->widgets, list) { + for_each_card_widgets(cmpnt->card, w) { if (w->dapm != dapm) continue; @@ -2431,7 +2436,7 @@ static ssize_t dapm_widget_show(struct device *dev, mutex_lock(&rtd->card->dapm_mutex); - for_each_rtd_codec_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { struct snd_soc_component *cmpnt = codec_dai->component; count += dapm_widget_show_component(cmpnt, buf + count); @@ -2491,7 +2496,7 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w, *next_w; - list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { + for_each_card_widgets_safe(dapm->card, w, next_w) { if (w->dapm != dapm) continue; snd_soc_dapm_free_widget(w); @@ -2506,7 +2511,7 @@ static struct snd_soc_dapm_widget *dapm_find_widget( struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *fallback = NULL; - list_for_each_entry(w, &dapm->card->widgets, list) { + for_each_card_widgets(dapm->card, w) { if (!strcmp(w->name, pin)) { if (w->dapm == dapm) return w; @@ -2624,10 +2629,7 @@ static int dapm_update_dai_unlocked(struct snd_pcm_substream *substream, struct snd_soc_dapm_widget *w; int ret; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + w = snd_soc_dai_get_widget(dai, dir); if (!w) return 0; @@ -2908,7 +2910,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, * find src and dest widgets over all widgets but favor a widget from * current DAPM context */ - list_for_each_entry(w, &dapm->card->widgets, list) { + for_each_card_widgets(dapm->card, w) { if (!wsink && !(strcmp(w->name, sink))) { wtsink = w; if (w->dapm == dapm) { @@ -3187,7 +3189,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); - list_for_each_entry(w, &card->widgets, list) + for_each_card_widgets(card, w) { if (w->new) continue; @@ -3394,7 +3396,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card); return change; } @@ -3499,7 +3501,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card); return change; } @@ -3604,6 +3606,9 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, ret = PTR_ERR(w->pinctrl); goto request_failed; } + + /* set to sleep_state when initializing */ + dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD); break; case snd_soc_dapm_clock_supply: w->clk = devm_clk_get(dapm->dev, w->name); @@ -3698,6 +3703,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, w->dapm = dapm; INIT_LIST_HEAD(&w->list); INIT_LIST_HEAD(&w->dirty); + /* see for_each_card_widgets */ list_add_tail(&w->list, &dapm->card->widgets); snd_soc_dapm_for_each_direction(dir) { @@ -4222,7 +4228,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) struct snd_soc_dai *dai; /* For each DAI widget... */ - list_for_each_entry(dai_w, &card->widgets, list) { + for_each_card_widgets(card, dai_w) { switch (dai_w->id) { case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: @@ -4241,7 +4247,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) dai = dai_w->priv; /* ...find all widgets with the same stream and link them */ - list_for_each_entry(w, &card->widgets, list) { + for_each_card_widgets(card, w) { if (w->dapm != dai_w->dapm) continue; @@ -4271,16 +4277,15 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) return 0; } -static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, - struct snd_soc_pcm_runtime *rtd) +static void dapm_add_valid_dai_widget(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *codec_dai, + struct snd_soc_dai *cpu_dai) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; struct snd_soc_dapm_widget *playback = NULL, *capture = NULL; struct snd_soc_dapm_widget *codec, *playback_cpu, *capture_cpu; struct snd_pcm_substream *substream; struct snd_pcm_str *streams = rtd->pcm->streams; - int i; if (rtd->dai_link->params) { playback_cpu = cpu_dai->capture_widget; @@ -4292,77 +4297,92 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, capture_cpu = capture; } - for_each_rtd_codec_dai(rtd, i, codec_dai) { - /* connect BE DAI playback if widgets are valid */ - codec = codec_dai->playback_widget; - - if (playback_cpu && codec) { - if (!playback) { - substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - playback = snd_soc_dapm_new_dai(card, substream, - "playback"); - if (IS_ERR(playback)) { - dev_err(rtd->dev, - "ASoC: Failed to create DAI %s: %ld\n", - codec_dai->name, - PTR_ERR(playback)); - continue; - } - - snd_soc_dapm_add_path(&card->dapm, playback_cpu, - playback, NULL, NULL); + /* connect BE DAI playback if widgets are valid */ + codec = codec_dai->playback_widget; + + if (playback_cpu && codec) { + if (!playback) { + substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + playback = snd_soc_dapm_new_dai(card, substream, + "playback"); + if (IS_ERR(playback)) { + dev_err(rtd->dev, + "ASoC: Failed to create DAI %s: %ld\n", + codec_dai->name, + PTR_ERR(playback)); + goto capture; } - dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", - cpu_dai->component->name, playback_cpu->name, - codec_dai->component->name, codec->name); - - snd_soc_dapm_add_path(&card->dapm, playback, codec, - NULL, NULL); + snd_soc_dapm_add_path(&card->dapm, playback_cpu, + playback, NULL, NULL); } - } - for_each_rtd_codec_dai(rtd, i, codec_dai) { - /* connect BE DAI capture if widgets are valid */ - codec = codec_dai->capture_widget; - - if (codec && capture_cpu) { - if (!capture) { - substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; - capture = snd_soc_dapm_new_dai(card, substream, - "capture"); - if (IS_ERR(capture)) { - dev_err(rtd->dev, - "ASoC: Failed to create DAI %s: %ld\n", - codec_dai->name, - PTR_ERR(capture)); - continue; - } - - snd_soc_dapm_add_path(&card->dapm, capture, - capture_cpu, NULL, NULL); + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + cpu_dai->component->name, playback_cpu->name, + codec_dai->component->name, codec->name); + + snd_soc_dapm_add_path(&card->dapm, playback, codec, + NULL, NULL); + } + +capture: + /* connect BE DAI capture if widgets are valid */ + codec = codec_dai->capture_widget; + + if (codec && capture_cpu) { + if (!capture) { + substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; + capture = snd_soc_dapm_new_dai(card, substream, + "capture"); + if (IS_ERR(capture)) { + dev_err(rtd->dev, + "ASoC: Failed to create DAI %s: %ld\n", + codec_dai->name, + PTR_ERR(capture)); + return; } - dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", - codec_dai->component->name, codec->name, - cpu_dai->component->name, capture_cpu->name); - - snd_soc_dapm_add_path(&card->dapm, codec, capture, - NULL, NULL); + snd_soc_dapm_add_path(&card->dapm, capture, + capture_cpu, NULL, NULL); } + + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + codec_dai->component->name, codec->name, + cpu_dai->component->name, capture_cpu->name); + + snd_soc_dapm_add_path(&card->dapm, codec, capture, + NULL, NULL); } } +static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai; + int i; + + if (rtd->num_cpus == 1) { + for_each_rtd_codec_dais(rtd, i, codec_dai) + dapm_add_valid_dai_widget(card, rtd, codec_dai, + rtd->cpu_dais[0]); + } else if (rtd->num_codecs == rtd->num_cpus) { + for_each_rtd_codec_dais(rtd, i, codec_dai) + dapm_add_valid_dai_widget(card, rtd, codec_dai, + rtd->cpu_dais[i]); + } else { + dev_err(card->dev, + "N cpus to M codecs link is not supported yet\n"); + } + +} + static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, int event) { struct snd_soc_dapm_widget *w; unsigned int ep; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + w = snd_soc_dai_get_widget(dai, stream); if (w) { dapm_mark_dirty(w, "stream event"); @@ -4413,12 +4433,11 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event) { - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; int i; - soc_dapm_dai_stream_event(rtd->cpu_dai, stream, event); - for_each_rtd_codec_dai(rtd, i, codec_dai) - soc_dapm_dai_stream_event(codec_dai, stream, event); + for_each_rtd_dais(rtd, i, dai) + soc_dapm_dai_stream_event(dai, stream, event); dapm_power_widgets(rtd->card, event); } @@ -4754,6 +4773,7 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, } INIT_LIST_HEAD(&dapm->list); + /* see for_each_card_dapms */ list_add(&dapm->list, &card->dapm_list); } EXPORT_SYMBOL_GPL(snd_soc_dapm_init); @@ -4767,7 +4787,7 @@ static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) mutex_lock(&card->dapm_mutex); - list_for_each_entry(w, &dapm->card->widgets, list) { + for_each_card_widgets(dapm->card, w) { if (w->dapm != dapm) continue; if (w->power) { @@ -4800,7 +4820,7 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card) { struct snd_soc_dapm_context *dapm; - list_for_each_entry(dapm, &card->dapm_list, list) { + for_each_card_dapms(card, dapm) { if (dapm != &card->dapm) { soc_dapm_shutdown_dapm(dapm); if (dapm->bias_level == SND_SOC_BIAS_STANDBY) diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 2cc25651661c..facf1922a714 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -62,6 +62,12 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, struct snd_dmaengine_dai_dma_data *dma_data; int ret; + if (rtd->num_cpus > 1) { + dev_err(rtd->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); @@ -118,6 +124,12 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, struct snd_dmaengine_dai_dma_data *dma_data; struct snd_pcm_hardware hw; + if (rtd->num_cpus > 1) { + dev_err(rtd->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + if (pcm->config && pcm->config->pcm_hardware) return snd_soc_set_runtime_hwparams(substream, pcm->config->pcm_hardware); @@ -185,6 +197,12 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( struct snd_dmaengine_dai_dma_data *dma_data; dma_filter_fn fn = NULL; + if (rtd->num_cpus > 1) { + dev_err(rtd->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return NULL; + } + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0]) @@ -237,7 +255,7 @@ static int dmaengine_pcm_new(struct snd_soc_component *component, max_buffer_size = SIZE_MAX; } - for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { + for_each_pcm_streams(i) { substream = rtd->pcm->streams[i].substream; if (!substream) continue; @@ -371,8 +389,7 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, dev = config->dma_dev; } - for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; - i++) { + for_each_pcm_streams(i) { if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) name = "rx-tx"; else @@ -401,8 +418,7 @@ static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm) { unsigned int i; - for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; - i++) { + for_each_pcm_streams(i) { if (!pcm->chan[i]) continue; dma_release_channel(pcm->chan[i]); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 2c59b3688ca0..e256d438ee68 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -28,6 +28,180 @@ #define DPCM_MAX_BE_USERS 8 +#ifdef CONFIG_DEBUG_FS +static const char *dpcm_state_string(enum snd_soc_dpcm_state state) +{ + switch (state) { + case SND_SOC_DPCM_STATE_NEW: + return "new"; + case SND_SOC_DPCM_STATE_OPEN: + return "open"; + case SND_SOC_DPCM_STATE_HW_PARAMS: + return "hw_params"; + case SND_SOC_DPCM_STATE_PREPARE: + return "prepare"; + case SND_SOC_DPCM_STATE_START: + return "start"; + case SND_SOC_DPCM_STATE_STOP: + return "stop"; + case SND_SOC_DPCM_STATE_SUSPEND: + return "suspend"; + case SND_SOC_DPCM_STATE_PAUSED: + return "paused"; + case SND_SOC_DPCM_STATE_HW_FREE: + return "hw_free"; + case SND_SOC_DPCM_STATE_CLOSE: + return "close"; + } + + return "unknown"; +} + +static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, + int stream, char *buf, size_t size) +{ + 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, + "[%s - %s]\n", fe->dai_link->name, + stream ? "Capture" : "Playback"); + + offset += scnprintf(buf + offset, size - offset, "State: %s\n", + dpcm_state_string(fe->dpcm[stream].state)); + + if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && + (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) + offset += scnprintf(buf + offset, size - offset, + "Hardware Params: " + "Format = %s, Channels = %d, Rate = %d\n", + snd_pcm_format_name(params_format(params)), + params_channels(params), + params_rate(params)); + + /* BEs state */ + offset += scnprintf(buf + offset, size - offset, "Backends:\n"); + + if (list_empty(&fe->dpcm[stream].be_clients)) { + offset += scnprintf(buf + offset, size - offset, + " No active DSP links\n"); + 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; + + offset += scnprintf(buf + offset, size - offset, + "- %s\n", be->dai_link->name); + + offset += scnprintf(buf + offset, size - offset, + " State: %s\n", + dpcm_state_string(be->dpcm[stream].state)); + + if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && + (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) + offset += scnprintf(buf + offset, size - offset, + " Hardware Params: " + "Format = %s, Channels = %d, Rate = %d\n", + snd_pcm_format_name(params_format(params)), + params_channels(params), + params_rate(params)); + } + spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); +out: + return offset; +} + +static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct snd_soc_pcm_runtime *fe = file->private_data; + ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0; + int stream; + char *buf; + + if (fe->num_cpus > 1) { + dev_err(fe->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + + buf = kmalloc(out_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for_each_pcm_streams(stream) + if (snd_soc_dai_stream_valid(fe->cpu_dai, stream)) + offset += dpcm_show_state(fe, stream, + buf + offset, + out_count - offset); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); + + kfree(buf); + return ret; +} + +static const struct file_operations dpcm_state_fops = { + .open = simple_open, + .read = dpcm_state_read_file, + .llseek = default_llseek, +}; + +void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) +{ + if (!rtd->dai_link) + return; + + if (!rtd->dai_link->dynamic) + return; + + if (!rtd->card->debugfs_card_root) + return; + + rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name, + rtd->card->debugfs_card_root); + + debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root, + rtd, &dpcm_state_fops); +} + +static void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm, int stream) +{ + char *name; + + name = kasprintf(GFP_KERNEL, "%s:%s", dpcm->be->dai_link->name, + stream ? "capture" : "playback"); + if (name) { + dpcm->debugfs_state = debugfs_create_dir( + name, dpcm->fe->debugfs_dpcm_root); + debugfs_create_u32("state", 0644, dpcm->debugfs_state, + &dpcm->state); + kfree(name); + } +} + +static void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm) +{ + debugfs_remove_recursive(dpcm->debugfs_state); +} + +#else +static inline void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm, + int stream) +{ +} + +static inline void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm) +{ +} +#endif + static int soc_rtd_startup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream) { @@ -82,6 +256,21 @@ static int soc_rtd_trigger(struct snd_soc_pcm_runtime *rtd, return 0; } +static void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, + int stream, int action) +{ + struct snd_soc_dai *dai; + int i; + + lockdep_assert_held(&rtd->card->pcm_mutex); + + for_each_rtd_dais(rtd, i, dai) { + dai->stream_active[stream] += action; + dai->active += action; + dai->component->active += action; + } +} + /** * snd_soc_runtime_activate() - Increment active count for PCM runtime components * @rtd: ASoC PCM runtime that is activated @@ -94,29 +283,9 @@ static int soc_rtd_trigger(struct snd_soc_pcm_runtime *rtd, */ void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; - int i; - - lockdep_assert_held(&rtd->card->pcm_mutex); - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active++; - for_each_rtd_codec_dai(rtd, i, codec_dai) - codec_dai->playback_active++; - } else { - cpu_dai->capture_active++; - for_each_rtd_codec_dai(rtd, i, codec_dai) - codec_dai->capture_active++; - } - - cpu_dai->active++; - cpu_dai->component->active++; - for_each_rtd_codec_dai(rtd, i, codec_dai) { - codec_dai->active++; - codec_dai->component->active++; - } + snd_soc_runtime_action(rtd, stream, 1); } +EXPORT_SYMBOL_GPL(snd_soc_runtime_activate); /** * snd_soc_runtime_deactivate() - Decrement active count for PCM runtime components @@ -130,29 +299,9 @@ void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream) */ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; - int i; - - lockdep_assert_held(&rtd->card->pcm_mutex); - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active--; - for_each_rtd_codec_dai(rtd, i, codec_dai) - codec_dai->playback_active--; - } else { - cpu_dai->capture_active--; - for_each_rtd_codec_dai(rtd, i, codec_dai) - codec_dai->capture_active--; - } - - cpu_dai->active--; - cpu_dai->component->active--; - for_each_rtd_codec_dai(rtd, i, codec_dai) { - codec_dai->component->active--; - codec_dai->active--; - } + snd_soc_runtime_action(rtd, stream, -1); } +EXPORT_SYMBOL_GPL(snd_soc_runtime_deactivate); /** * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay @@ -287,8 +436,8 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; + struct snd_soc_dai *cpu_dai; unsigned int rate, channels, sample_bits, symmetry, i; rate = params_rate(params); @@ -296,40 +445,51 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, sample_bits = snd_pcm_format_physical_width(params_format(params)); /* reject unmatched parameters when applying symmetry */ - symmetry = cpu_dai->driver->symmetric_rates || - rtd->dai_link->symmetric_rates; + symmetry = rtd->dai_link->symmetric_rates; - for_each_rtd_codec_dai(rtd, i, codec_dai) - symmetry |= codec_dai->driver->symmetric_rates; + for_each_rtd_cpu_dais(rtd, i, dai) + symmetry |= dai->driver->symmetric_rates; - if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { - dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", - cpu_dai->rate, rate); - return -EINVAL; + if (symmetry) { + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + if (cpu_dai->rate && cpu_dai->rate != rate) { + dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", + cpu_dai->rate, rate); + return -EINVAL; + } + } } - symmetry = cpu_dai->driver->symmetric_channels || - rtd->dai_link->symmetric_channels; + symmetry = rtd->dai_link->symmetric_channels; - for_each_rtd_codec_dai(rtd, i, codec_dai) - symmetry |= codec_dai->driver->symmetric_channels; + for_each_rtd_dais(rtd, i, dai) + symmetry |= dai->driver->symmetric_channels; - if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { - dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", - cpu_dai->channels, channels); - return -EINVAL; + if (symmetry) { + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + if (cpu_dai->channels && + cpu_dai->channels != channels) { + dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", + cpu_dai->channels, channels); + return -EINVAL; + } + } } - symmetry = cpu_dai->driver->symmetric_samplebits || - rtd->dai_link->symmetric_samplebits; + symmetry = rtd->dai_link->symmetric_samplebits; - for_each_rtd_codec_dai(rtd, i, codec_dai) - symmetry |= codec_dai->driver->symmetric_samplebits; + for_each_rtd_dais(rtd, i, dai) + symmetry |= dai->driver->symmetric_samplebits; - if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { - dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", - cpu_dai->sample_bits, sample_bits); - return -EINVAL; + if (symmetry) { + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + if (cpu_dai->sample_bits && + cpu_dai->sample_bits != sample_bits) { + dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", + cpu_dai->sample_bits, sample_bits); + return -EINVAL; + } + } } return 0; @@ -338,20 +498,19 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver; struct snd_soc_dai_link *link = rtd->dai_link; - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; unsigned int symmetry, i; - symmetry = cpu_driver->symmetric_rates || link->symmetric_rates || - cpu_driver->symmetric_channels || link->symmetric_channels || - cpu_driver->symmetric_samplebits || link->symmetric_samplebits; + symmetry = link->symmetric_rates || + link->symmetric_channels || + link->symmetric_samplebits; - for_each_rtd_codec_dai(rtd, i, codec_dai) + for_each_rtd_dais(rtd, i, dai) symmetry = symmetry || - codec_dai->driver->symmetric_rates || - codec_dai->driver->symmetric_channels || - codec_dai->driver->symmetric_samplebits; + dai->driver->symmetric_rates || + dai->driver->symmetric_channels || + dai->driver->symmetric_samplebits; return symmetry; } @@ -373,77 +532,98 @@ static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits) static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; + struct snd_soc_pcm_stream *pcm_codec, *pcm_cpu; + int stream = substream->stream; int i; - unsigned int bits = 0, cpu_bits; + unsigned int bits = 0, cpu_bits = 0; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (codec_dai->driver->playback.sig_bits == 0) { - bits = 0; - break; - } - bits = max(codec_dai->driver->playback.sig_bits, bits); + for_each_rtd_codec_dais(rtd, i, codec_dai) { + pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream); + + if (pcm_codec->sig_bits == 0) { + bits = 0; + break; } - cpu_bits = cpu_dai->driver->playback.sig_bits; - } else { - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (codec_dai->driver->capture.sig_bits == 0) { - bits = 0; - break; - } - bits = max(codec_dai->driver->capture.sig_bits, bits); + bits = max(pcm_codec->sig_bits, bits); + } + + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream); + + if (pcm_cpu->sig_bits == 0) { + cpu_bits = 0; + break; } - cpu_bits = cpu_dai->driver->capture.sig_bits; + cpu_bits = max(pcm_cpu->sig_bits, cpu_bits); } soc_pcm_set_msb(substream, bits); soc_pcm_set_msb(substream, cpu_bits); } -static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) +/** + * snd_soc_runtime_calc_hw() - Calculate hw limits for a PCM stream + * @rtd: ASoC PCM runtime + * @hw: PCM hardware parameters (output) + * @stream: Direction of the PCM stream + * + * Calculates the subset of stream parameters supported by all DAIs + * associated with the PCM stream. + */ +int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hardware *hw, int stream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_pcm_hardware *hw = &runtime->hw; - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai; - struct snd_soc_dai_driver *cpu_dai_drv = rtd->cpu_dai->driver; - struct snd_soc_dai_driver *codec_dai_drv; + struct snd_soc_dai *cpu_dai; struct snd_soc_pcm_stream *codec_stream; struct snd_soc_pcm_stream *cpu_stream; unsigned int chan_min = 0, chan_max = UINT_MAX; + unsigned int cpu_chan_min = 0, cpu_chan_max = UINT_MAX; unsigned int rate_min = 0, rate_max = UINT_MAX; - unsigned int rates = UINT_MAX; + unsigned int cpu_rate_min = 0, cpu_rate_max = UINT_MAX; + unsigned int rates = UINT_MAX, cpu_rates = UINT_MAX; u64 formats = ULLONG_MAX; int i; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; + /* first calculate min/max only for CPUs in the DAI link */ + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - /* first calculate min/max only for CODECs in the DAI link */ - for_each_rtd_codec_dai(rtd, i, codec_dai) { + /* + * Skip CPUs which don't support the current stream type. + * Otherwise, since the rate, channel, and format values will + * zero in that case, we would have no usable settings left, + * causing the resulting setup to fail. + */ + if (!snd_soc_dai_stream_valid(cpu_dai, stream)) + continue; + + cpu_stream = snd_soc_dai_get_pcm_stream(cpu_dai, stream); + + cpu_chan_min = max(cpu_chan_min, cpu_stream->channels_min); + cpu_chan_max = min(cpu_chan_max, cpu_stream->channels_max); + cpu_rate_min = max(cpu_rate_min, cpu_stream->rate_min); + cpu_rate_max = min_not_zero(cpu_rate_max, cpu_stream->rate_max); + formats &= cpu_stream->formats; + cpu_rates = snd_pcm_rate_mask_intersect(cpu_stream->rates, + cpu_rates); + } + + /* second calculate min/max only for CODECs in the DAI link */ + for_each_rtd_codec_dais(rtd, i, codec_dai) { /* * Skip CODECs which don't support the current stream type. * Otherwise, since the rate, channel, and format values will * zero in that case, we would have no usable settings left, * causing the resulting setup to fail. - * At least one CODEC should match, otherwise we should have - * bailed out on a higher level, since there would be no - * CODEC to support the transfer direction in that case. */ - if (!snd_soc_dai_stream_valid(codec_dai, - substream->stream)) + if (!snd_soc_dai_stream_valid(codec_dai, stream)) continue; - codec_dai_drv = codec_dai->driver; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - codec_stream = &codec_dai_drv->playback; - else - codec_stream = &codec_dai_drv->capture; + codec_stream = snd_soc_dai_get_pcm_stream(codec_dai, stream); + chan_min = max(chan_min, codec_stream->channels_min); chan_max = min(chan_max, codec_stream->channels_max); rate_min = max(rate_min, codec_stream->rate_min); @@ -452,74 +632,107 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) rates = snd_pcm_rate_mask_intersect(codec_stream->rates, rates); } + /* Verify both a valid CPU DAI and a valid CODEC DAI were found */ + if (!chan_min || !cpu_chan_min) + return -EINVAL; + /* * chan min/max cannot be enforced if there are multiple CODEC DAIs - * connected to a single CPU DAI, use CPU DAI's directly and let + * connected to CPU DAI(s), use CPU DAI's directly and let * channel allocation be fixed up later */ if (rtd->num_codecs > 1) { - chan_min = cpu_stream->channels_min; - chan_max = cpu_stream->channels_max; + chan_min = cpu_chan_min; + chan_max = cpu_chan_max; } - hw->channels_min = max(chan_min, cpu_stream->channels_min); - hw->channels_max = min(chan_max, cpu_stream->channels_max); - if (hw->formats) - hw->formats &= formats & cpu_stream->formats; - else - hw->formats = formats & cpu_stream->formats; - hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_stream->rates); + /* finally find a intersection between CODECs and CPUs */ + hw->channels_min = max(chan_min, cpu_chan_min); + hw->channels_max = min(chan_max, cpu_chan_max); + hw->formats = formats; + hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_rates); - snd_pcm_limit_hw_rates(runtime); + snd_pcm_hw_limit_rates(hw); - hw->rate_min = max(hw->rate_min, cpu_stream->rate_min); + hw->rate_min = max(hw->rate_min, cpu_rate_min); hw->rate_min = max(hw->rate_min, rate_min); - hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max); + hw->rate_max = min_not_zero(hw->rate_max, cpu_rate_max); hw->rate_max = min_not_zero(hw->rate_max, rate_max); + + return 0; } +EXPORT_SYMBOL_GPL(snd_soc_runtime_calc_hw); -static int soc_pcm_components_open(struct snd_pcm_substream *substream, - struct snd_soc_component **last) +static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) { + struct snd_pcm_hardware *hw = &substream->runtime->hw; struct snd_soc_pcm_runtime *rtd = substream->private_data; + u64 formats = hw->formats; + + /* + * At least one CPU and one CODEC should match. Otherwise, we should + * have bailed out on a higher level, since there would be no CPU or + * CODEC to support the transfer direction in that case. + */ + snd_soc_runtime_calc_hw(rtd, hw, substream->stream); + + if (formats) + hw->formats &= formats; +} + +static int soc_pcm_components_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *last = NULL; struct snd_soc_component *component; int i, ret = 0; for_each_rtd_components(rtd, i, component) { - *last = component; + last = component; ret = snd_soc_component_module_get_when_open(component); if (ret < 0) { dev_err(component->dev, "ASoC: can't get module %s\n", component->name); - return ret; + break; } ret = snd_soc_component_open(component, substream); if (ret < 0) { + snd_soc_component_module_put_when_close(component); dev_err(component->dev, "ASoC: can't open component %s: %d\n", component->name, ret); - return ret; + break; } } - *last = NULL; - return 0; + + if (ret < 0) { + /* rollback on error */ + for_each_rtd_components(rtd, i, component) { + if (component == last) + break; + + snd_soc_component_close(component, substream); + snd_soc_component_module_put_when_close(component); + } + } + + return ret; } -static int soc_pcm_components_close(struct snd_pcm_substream *substream, - struct snd_soc_component *last) +static int soc_pcm_components_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - int i, ret = 0; + int i, r, ret = 0; for_each_rtd_components(rtd, i, component) { - if (component == last) - break; + r = snd_soc_component_close(component, substream); + if (r < 0) + ret = r; /* use last ret */ - ret |= snd_soc_component_close(component, substream); snd_soc_component_module_put_when_close(component); } @@ -527,6 +740,45 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream, } /* + * Called by ALSA when a PCM substream is closed. Private data can be + * freed here. The cpu DAI, codec DAI, machine and components are also + * shutdown. + */ +static int soc_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + 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_runtime_deactivate(rtd, substream->stream); + + for_each_rtd_dais(rtd, i, dai) + snd_soc_dai_shutdown(dai, substream); + + soc_rtd_shutdown(rtd, substream); + + soc_pcm_components_close(substream); + + snd_soc_dapm_stream_stop(rtd, substream->stream); + + mutex_unlock(&rtd->card->pcm_mutex); + + for_each_rtd_components(rtd, i, component) { + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + } + + for_each_rtd_components(rtd, i, component) + if (!component->active) + pinctrl_pm_select_sleep_state(component->dev); + + return 0; +} + +/* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls * startup for the cpu DAI, component, machine and codec DAI. @@ -536,9 +788,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; const char *codec_dai_name = "multicodec"; + const char *cpu_dai_name = "multicpu"; int i, ret = 0; for_each_rtd_components(rtd, i, component) @@ -549,38 +801,31 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - /* startup the audio subsystem */ - ret = snd_soc_dai_startup(cpu_dai, substream); - if (ret < 0) { - dev_err(cpu_dai->dev, "ASoC: can't open interface %s: %d\n", - cpu_dai->name, ret); - goto out; - } - - ret = soc_pcm_components_open(substream, &component); + ret = soc_pcm_components_open(substream); if (ret < 0) goto component_err; - for_each_rtd_codec_dai(rtd, i, codec_dai) { - ret = snd_soc_dai_startup(codec_dai, substream); + ret = soc_rtd_startup(rtd, substream); + if (ret < 0) { + pr_err("ASoC: %s startup failed: %d\n", + rtd->dai_link->name, ret); + goto rtd_startup_err; + } + + /* startup the audio subsystem */ + for_each_rtd_dais(rtd, i, dai) { + ret = snd_soc_dai_startup(dai, substream); if (ret < 0) { - dev_err(codec_dai->dev, - "ASoC: can't open codec %s: %d\n", - codec_dai->name, ret); - goto codec_dai_err; + dev_err(dai->dev, + "ASoC: can't open DAI %s: %d\n", + dai->name, ret); + goto config_err; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - codec_dai->tx_mask = 0; + dai->tx_mask = 0; else - codec_dai->rx_mask = 0; - } - - ret = soc_rtd_startup(rtd, substream); - if (ret < 0) { - pr_err("ASoC: %s startup failed: %d\n", - rtd->dai_link->name, ret); - goto machine_err; + dai->rx_mask = 0; } /* Dynamic PCM DAI links compat checks use dynamic capabilities */ @@ -593,46 +838,43 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) if (rtd->num_codecs == 1) codec_dai_name = rtd->codec_dai->name; + if (rtd->num_cpus == 1) + cpu_dai_name = rtd->cpu_dai->name; + if (soc_pcm_has_symmetry(substream)) runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; ret = -EINVAL; if (!runtime->hw.rates) { printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); goto config_err; } if (!runtime->hw.formats) { printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); goto config_err; } if (!runtime->hw.channels_min || !runtime->hw.channels_max || runtime->hw.channels_min > runtime->hw.channels_max) { printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); goto config_err; } soc_pcm_apply_msb(substream); /* Symmetry only applies if we've already got an active stream. */ - if (cpu_dai->active) { - ret = soc_pcm_apply_symmetry(substream, cpu_dai); - if (ret != 0) - goto config_err; - } - - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (codec_dai->active) { - ret = soc_pcm_apply_symmetry(substream, codec_dai); + for_each_rtd_dais(rtd, i, dai) { + if (dai->active) { + ret = soc_pcm_apply_symmetry(substream, dai); if (ret != 0) goto config_err; } } pr_debug("ASoC: %s <-> %s info:\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates); pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min, runtime->hw.channels_max); @@ -647,20 +889,13 @@ dynamic: return 0; config_err: - soc_rtd_shutdown(rtd, substream); - -machine_err: - i = rtd->num_codecs; - -codec_dai_err: - for_each_rtd_codec_dai_rollback(rtd, i, codec_dai) - snd_soc_dai_shutdown(codec_dai, substream); + for_each_rtd_dais(rtd, i, dai) + snd_soc_dai_shutdown(dai, substream); + soc_rtd_shutdown(rtd, substream); +rtd_startup_err: + soc_pcm_components_close(substream); component_err: - soc_pcm_components_close(substream, component); - - snd_soc_dai_shutdown(cpu_dai, substream); -out: mutex_unlock(&rtd->card->pcm_mutex); for_each_rtd_components(rtd, i, component) { @@ -686,59 +921,6 @@ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) } /* - * Called by ALSA when a PCM substream is closed. Private data can be - * freed here. The cpu DAI, codec DAI, machine and components are also - * shutdown. - */ -static int soc_pcm_close(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; - int i; - - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - - snd_soc_runtime_deactivate(rtd, substream->stream); - - /* clear the corresponding DAIs rate when inactive */ - if (!cpu_dai->active) - cpu_dai->rate = 0; - - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (!codec_dai->active) - codec_dai->rate = 0; - } - - snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); - - snd_soc_dai_shutdown(cpu_dai, substream); - - for_each_rtd_codec_dai(rtd, i, codec_dai) - snd_soc_dai_shutdown(codec_dai, substream); - - soc_rtd_shutdown(rtd, substream); - - soc_pcm_components_close(substream, NULL); - - snd_soc_dapm_stream_stop(rtd, substream->stream); - - mutex_unlock(&rtd->card->pcm_mutex); - - for_each_rtd_components(rtd, i, component) { - pm_runtime_mark_last_busy(component->dev); - pm_runtime_put_autosuspend(component->dev); - } - - for_each_rtd_components(rtd, i, component) - if (!component->active) - pinctrl_pm_select_sleep_state(component->dev); - - return 0; -} - -/* * Called by ALSA when the PCM substream is prepared, can set format, sample * rate, etc. This function is non atomic and can be called multiple times, * it can refer to the runtime info. @@ -747,8 +929,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; int i, ret = 0; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -769,23 +950,15 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - for_each_rtd_codec_dai(rtd, i, codec_dai) { - ret = snd_soc_dai_prepare(codec_dai, substream); + for_each_rtd_dais(rtd, i, dai) { + ret = snd_soc_dai_prepare(dai, substream); if (ret < 0) { - dev_err(codec_dai->dev, - "ASoC: codec DAI prepare error: %d\n", - ret); + dev_err(dai->dev, + "ASoC: DAI prepare error: %d\n", ret); goto out; } } - ret = snd_soc_dai_prepare(cpu_dai, substream); - if (ret < 0) { - dev_err(cpu_dai->dev, - "ASoC: cpu DAI prepare error: %d\n", ret); - goto out; - } - /* cancel any delayed stream shutdown that is pending */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && rtd->pop_wait) { @@ -796,10 +969,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dapm_stream_event(rtd, substream->stream, SND_SOC_DAPM_STREAM_START); - for_each_rtd_codec_dai(rtd, i, codec_dai) - snd_soc_dai_digital_mute(codec_dai, 0, - substream->stream); - snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream); + for_each_rtd_dais(rtd, i, dai) + snd_soc_dai_digital_mute(dai, 0, substream->stream); out: mutex_unlock(&rtd->card->pcm_mutex); @@ -822,13 +993,15 @@ static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - int i, ret = 0; + int i, r, ret = 0; for_each_rtd_components(rtd, i, component) { if (component == last) break; - ret |= snd_soc_component_hw_free(component, substream); + r = snd_soc_component_hw_free(component, substream); + if (r < 0) + ret = r; /* use last ret */ } return ret; @@ -844,7 +1017,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; @@ -861,7 +1034,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, goto out; } - for_each_rtd_codec_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { struct snd_pcm_hw_params codec_params; /* @@ -908,17 +1081,26 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, snd_soc_dapm_update_dai(substream, &codec_params, codec_dai); } - ret = snd_soc_dai_hw_params(cpu_dai, substream, params); - if (ret < 0) - goto interface_err; + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + /* + * Skip CPUs which don't support the current stream + * type. See soc_pcm_init_runtime_hw() for more details + */ + if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) + continue; - /* store the parameters for each DAIs */ - cpu_dai->rate = params_rate(params); - cpu_dai->channels = params_channels(params); - cpu_dai->sample_bits = - snd_pcm_format_physical_width(params_format(params)); + ret = snd_soc_dai_hw_params(cpu_dai, substream, params); + if (ret < 0) + goto interface_err; + + /* store the parameters for each DAI */ + cpu_dai->rate = params_rate(params); + cpu_dai->channels = params_channels(params); + cpu_dai->sample_bits = + snd_pcm_format_physical_width(params_format(params)); - snd_soc_dapm_update_dai(substream, params, cpu_dai); + snd_soc_dapm_update_dai(substream, params, cpu_dai); + } for_each_rtd_components(rtd, i, component) { ret = snd_soc_component_hw_params(component, substream, params); @@ -938,14 +1120,21 @@ out: component_err: soc_pcm_components_hw_free(substream, component); - snd_soc_dai_hw_free(cpu_dai, substream); - cpu_dai->rate = 0; + i = rtd->num_cpus; interface_err: + for_each_rtd_cpu_dais_rollback(rtd, i, cpu_dai) { + if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) + continue; + + snd_soc_dai_hw_free(cpu_dai, substream); + cpu_dai->rate = 0; + } + i = rtd->num_codecs; codec_err: - for_each_rtd_codec_dai_rollback(rtd, i, codec_dai) { + for_each_rtd_codec_dais_rollback(rtd, i, codec_dai) { if (!snd_soc_dai_stream_valid(codec_dai, substream->stream)) continue; @@ -965,34 +1154,23 @@ codec_err: static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; - bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_soc_dai *dai; int i; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); /* clear the corresponding DAIs parameters when going to be inactive */ - if (cpu_dai->active == 1) { - cpu_dai->rate = 0; - cpu_dai->channels = 0; - cpu_dai->sample_bits = 0; - } + for_each_rtd_dais(rtd, i, dai) { + int active = dai->stream_active[substream->stream]; - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (codec_dai->active == 1) { - codec_dai->rate = 0; - codec_dai->channels = 0; - codec_dai->sample_bits = 0; + if (dai->active == 1) { + dai->rate = 0; + dai->channels = 0; + dai->sample_bits = 0; } - } - /* apply codec digital mute */ - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if ((playback && codec_dai->playback_active == 1) || - (!playback && codec_dai->capture_active == 1)) - snd_soc_dai_digital_mute(codec_dai, 1, - substream->stream); + if (active == 1) + snd_soc_dai_digital_mute(dai, 1, substream->stream); } /* free any machine hw params */ @@ -1002,15 +1180,13 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) soc_pcm_components_hw_free(substream, NULL); /* now free hw params for the DAIs */ - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (!snd_soc_dai_stream_valid(codec_dai, substream->stream)) + for_each_rtd_dais(rtd, i, dai) { + if (!snd_soc_dai_stream_valid(dai, substream->stream)) continue; - snd_soc_dai_hw_free(codec_dai, substream); + snd_soc_dai_hw_free(dai, substream); } - snd_soc_dai_hw_free(cpu_dai, substream); - mutex_unlock(&rtd->card->pcm_mutex); return 0; } @@ -1019,8 +1195,7 @@ static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; int i, ret; ret = soc_rtd_trigger(rtd, substream, cmd); @@ -1033,12 +1208,8 @@ static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd) return ret; } - ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); - if (ret < 0) - return ret; - - for_each_rtd_codec_dai(rtd, i, codec_dai) { - ret = snd_soc_dai_trigger(codec_dai, substream, cmd); + for_each_rtd_dais(rtd, i, dai) { + ret = snd_soc_dai_trigger(dai, substream, cmd); if (ret < 0) return ret; } @@ -1050,20 +1221,15 @@ static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; int i, ret; - for_each_rtd_codec_dai(rtd, i, codec_dai) { - ret = snd_soc_dai_trigger(codec_dai, substream, cmd); + for_each_rtd_dais(rtd, i, dai) { + ret = snd_soc_dai_trigger(dai, substream, cmd); if (ret < 0) return ret; } - ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); - if (ret < 0) - return ret; - for_each_rtd_components(rtd, i, component) { ret = snd_soc_component_trigger(component, substream, cmd); if (ret < 0) @@ -1103,20 +1269,15 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; int i, ret; - for_each_rtd_codec_dai(rtd, i, codec_dai) { - ret = snd_soc_dai_bespoke_trigger(codec_dai, substream, cmd); + for_each_rtd_dais(rtd, i, dai) { + ret = snd_soc_dai_bespoke_trigger(dai, substream, cmd); if (ret < 0) return ret; } - ret = snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd); - if (ret < 0) - return ret; - return 0; } /* @@ -1127,12 +1288,13 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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 */ @@ -1143,9 +1305,13 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) /* base delay if assigned in pointer callback */ delay = runtime->delay; - delay += snd_soc_dai_delay(cpu_dai, substream); + 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_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { codec_delay = max(codec_delay, snd_soc_dai_delay(codec_dai, substream)); } @@ -1162,9 +1328,6 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, { struct snd_soc_dpcm *dpcm; unsigned long flags; -#ifdef CONFIG_DEBUG_FS - char *name; -#endif /* only add new dpcms */ for_each_dpcm_be(fe, stream, dpcm) { @@ -1189,17 +1352,8 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, stream ? "capture" : "playback", fe->dai_link->name, stream ? "<-" : "->", be->dai_link->name); -#ifdef CONFIG_DEBUG_FS - name = kasprintf(GFP_KERNEL, "%s:%s", be->dai_link->name, - stream ? "capture" : "playback"); - if (name) { - dpcm->debugfs_state = debugfs_create_dir(name, - fe->debugfs_dpcm_root); - debugfs_create_u32("state", 0644, dpcm->debugfs_state, - &dpcm->state); - kfree(name); - } -#endif + dpcm_create_debugfs_state(dpcm, stream); + return 1; } @@ -1252,9 +1406,8 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) /* BEs still alive need new FE */ dpcm_be_reparent(fe, dpcm->be, stream); -#ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(dpcm->debugfs_state); -#endif + dpcm_remove_debugfs_state(dpcm); + spin_lock_irqsave(&fe->card->dpcm_lock, flags); list_del(&dpcm->list_be); list_del(&dpcm->list_fe); @@ -1268,74 +1421,41 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, struct snd_soc_dapm_widget *widget, int stream) { struct snd_soc_pcm_runtime *be; + struct snd_soc_dapm_widget *w; struct snd_soc_dai *dai; int i; dev_dbg(card->dev, "ASoC: find BE for widget %s\n", widget->name); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - for_each_card_rtds(card, be) { - - if (!be->dai_link->no_pcm) - continue; - - dev_dbg(card->dev, "ASoC: try BE : %s\n", - be->cpu_dai->playback_widget ? - be->cpu_dai->playback_widget->name : "(not set)"); + for_each_card_rtds(card, be) { - if (be->cpu_dai->playback_widget == widget) - return be; - - for_each_rtd_codec_dai(be, i, dai) { - if (dai->playback_widget == widget) - return be; - } - } - } else { - - for_each_card_rtds(card, be) { + if (!be->dai_link->no_pcm) + continue; - if (!be->dai_link->no_pcm) - continue; + for_each_rtd_dais(be, i, dai) { + w = snd_soc_dai_get_widget(dai, stream); - dev_dbg(card->dev, "ASoC: try BE %s\n", - be->cpu_dai->capture_widget ? - be->cpu_dai->capture_widget->name : "(not set)"); + dev_dbg(card->dev, "ASoC: try BE : %s\n", + w ? w->name : "(not set)"); - if (be->cpu_dai->capture_widget == widget) + if (w == widget) return be; - - for_each_rtd_codec_dai(be, i, dai) { - if (dai->capture_widget == widget) - return be; - } } } - /* dai link name and stream name set correctly ? */ - dev_err(card->dev, "ASoC: can't get %s BE for %s\n", - stream ? "capture" : "playback", widget->name); + /* Widget provided is not a BE */ return NULL; } -static inline struct snd_soc_dapm_widget * - dai_get_widget(struct snd_soc_dai *dai, int stream) -{ - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - return dai->playback_widget; - else - return dai->capture_widget; -} - static int widget_in_list(struct snd_soc_dapm_widget_list *list, struct snd_soc_dapm_widget *widget) { + struct snd_soc_dapm_widget *w; int i; - for (i = 0; i < list->num_widgets; i++) { - if (widget == list->widgets[i]) + for_each_dapm_widgets(list, i, w) + if (widget == w) return 1; - } return 0; } @@ -1345,36 +1465,17 @@ static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, { struct snd_soc_card *card = widget->dapm->card; struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai *dai; - int i; + int stream; - if (dir == SND_SOC_DAPM_DIR_OUT) { - for_each_card_rtds(card, rtd) { - if (!rtd->dai_link->no_pcm) - continue; - - if (rtd->cpu_dai->playback_widget == widget) - return true; - - for_each_rtd_codec_dai(rtd, i, dai) { - if (dai->playback_widget == widget) - return true; - } - } - } else { /* SND_SOC_DAPM_DIR_IN */ - for_each_card_rtds(card, rtd) { - if (!rtd->dai_link->no_pcm) - continue; - - if (rtd->cpu_dai->capture_widget == widget) - return true; + /* adjust dir to stream */ + if (dir == SND_SOC_DAPM_DIR_OUT) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; - for_each_rtd_codec_dai(rtd, i, dai) { - if (dai->capture_widget == widget) - return true; - } - } - } + rtd = dpcm_get_be(card, widget, stream); + if (rtd) + return true; return false; } @@ -1385,6 +1486,12 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe, struct snd_soc_dai *cpu_dai = fe->cpu_dai; int paths; + if (fe->num_cpus > 1) { + dev_err(fe->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + /* get number of valid DAI paths and their widgets */ paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list, dpcm_end_walk_at_be); @@ -1395,37 +1502,42 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe, return paths; } -static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, - struct snd_soc_dapm_widget_list **list_) +void dpcm_path_put(struct snd_soc_dapm_widget_list **list) +{ + snd_soc_dapm_dai_free_widgets(list); +} + +static bool dpcm_be_is_active(struct snd_soc_dpcm *dpcm, int stream, + struct snd_soc_dapm_widget_list *list) { - struct snd_soc_dpcm *dpcm; - struct snd_soc_dapm_widget_list *list = *list_; struct snd_soc_dapm_widget *widget; struct snd_soc_dai *dai; - int prune = 0; - int do_prune; - - /* Destroy any old FE <--> BE connections */ - for_each_dpcm_be(fe, stream, dpcm) { - unsigned int i; + unsigned int i; - /* is there a valid CPU DAI widget for this BE */ - widget = dai_get_widget(dpcm->be->cpu_dai, stream); + /* is there a valid DAI widget for this BE */ + for_each_rtd_dais(dpcm->be, i, dai) { + widget = snd_soc_dai_get_widget(dai, stream); - /* prune the BE if it's no longer in our active list */ + /* + * The BE is pruned only if none of the dai + * widgets are in the active list. + */ if (widget && widget_in_list(list, widget)) - continue; + return true; + } - /* is there a valid CODEC DAI widget for this BE */ - do_prune = 1; - for_each_rtd_codec_dai(dpcm->be, i, dai) { - widget = dai_get_widget(dai, stream); + return false; +} - /* prune the BE if it's no longer in our active list */ - if (widget && widget_in_list(list, widget)) - do_prune = 0; - } - if (!do_prune) +static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, + struct snd_soc_dapm_widget_list **list_) +{ + struct snd_soc_dpcm *dpcm; + int prune = 0; + + /* Destroy any old FE <--> BE connections */ + for_each_dpcm_be(fe, stream, dpcm) { + if (dpcm_be_is_active(dpcm, stream, *list_)) continue; dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n", @@ -1446,12 +1558,13 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_card *card = fe->card; struct snd_soc_dapm_widget_list *list = *list_; struct snd_soc_pcm_runtime *be; + struct snd_soc_dapm_widget *widget; int i, new = 0, err; /* Create any new FE <--> BE connections */ - for (i = 0; i < list->num_widgets; i++) { + for_each_dapm_widgets(list, i, widget) { - switch (list->widgets[i]->id) { + switch (widget->id) { case snd_soc_dapm_dai_in: if (stream != SNDRV_PCM_STREAM_PLAYBACK) continue; @@ -1465,17 +1578,13 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, } /* is there a valid BE rtd for this widget */ - be = dpcm_get_be(card, list->widgets[i], stream); + be = dpcm_get_be(card, widget, stream); if (!be) { dev_err(fe->dev, "ASoC: no BE found for %s\n", - list->widgets[i]->name); + widget->name); continue; } - /* make sure BE is a real BE */ - if (!be->dai_link->no_pcm) - continue; - /* don't connect if FE is not running */ if (!fe->dpcm[stream].runtime && !fe->fe_compr) continue; @@ -1484,7 +1593,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, err = dpcm_be_connect(fe, be, stream); if (err < 0) { dev_err(fe->dev, "ASoC: can't connect %s\n", - list->widgets[i]->name); + widget->name); break; } else if (err == 0) /* already connected */ continue; @@ -1671,11 +1780,10 @@ static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream, for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; int i; - for_each_rtd_codec_dai(be, i, dai) { + for_each_rtd_codec_dais(be, i, dai) { /* * Skip CODECs which don't support the current stream * type. See soc_pcm_init_runtime_hw() for more details @@ -1683,11 +1791,7 @@ static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream, if (!snd_soc_dai_stream_valid(dai, stream)) continue; - codec_dai_drv = dai->driver; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - codec_stream = &codec_dai_drv->playback; - else - codec_stream = &codec_dai_drv->capture; + codec_stream = snd_soc_dai_get_pcm_stream(dai, stream); *formats &= codec_stream->formats; } @@ -1712,30 +1816,33 @@ static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream, for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; - struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; struct snd_soc_pcm_stream *cpu_stream; + struct snd_soc_dai *dai; + int i; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; + for_each_rtd_cpu_dais(be, i, dai) { + /* + * Skip CPUs which don't support the current stream + * type. See soc_pcm_init_runtime_hw() for more details + */ + if (!snd_soc_dai_stream_valid(dai, stream)) + continue; + + cpu_stream = snd_soc_dai_get_pcm_stream(dai, stream); - *channels_min = max(*channels_min, cpu_stream->channels_min); - *channels_max = min(*channels_max, cpu_stream->channels_max); + *channels_min = max(*channels_min, + cpu_stream->channels_min); + *channels_max = min(*channels_max, + cpu_stream->channels_max); + } /* * chan min/max cannot be enforced if there are multiple CODEC * DAIs connected to a single CPU DAI, use CPU DAI's directly */ if (be->num_codecs == 1) { - codec_dai_drv = be->codec_dais[0]->driver; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - codec_stream = &codec_dai_drv->playback; - else - codec_stream = &codec_dai_drv->capture; + codec_stream = snd_soc_dai_get_pcm_stream(be->codec_dais[0], stream); *channels_min = max(*channels_min, codec_stream->channels_min); @@ -1764,41 +1871,23 @@ static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream, for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; - struct snd_soc_dai_driver *codec_dai_drv; - struct snd_soc_pcm_stream *codec_stream; - struct snd_soc_pcm_stream *cpu_stream; + struct snd_soc_pcm_stream *pcm; struct snd_soc_dai *dai; int i; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; - - *rate_min = max(*rate_min, cpu_stream->rate_min); - *rate_max = min_not_zero(*rate_max, cpu_stream->rate_max); - *rates = snd_pcm_rate_mask_intersect(*rates, cpu_stream->rates); - - for_each_rtd_codec_dai(be, i, dai) { + for_each_rtd_dais(be, i, dai) { /* - * Skip CODECs which don't support the current stream + * Skip DAIs which don't support the current stream * type. See soc_pcm_init_runtime_hw() for more details */ if (!snd_soc_dai_stream_valid(dai, stream)) continue; - codec_dai_drv = dai->driver; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - codec_stream = &codec_dai_drv->playback; - else - codec_stream = &codec_dai_drv->capture; + pcm = snd_soc_dai_get_pcm_stream(dai, stream); - *rate_min = max(*rate_min, codec_stream->rate_min); - *rate_max = min_not_zero(*rate_max, - codec_stream->rate_max); - *rates = snd_pcm_rate_mask_intersect(*rates, - codec_stream->rates); + *rate_min = max(*rate_min, pcm->rate_min); + *rate_max = min_not_zero(*rate_max, pcm->rate_max); + *rates = snd_pcm_rate_mask_intersect(*rates, pcm->rates); } } } @@ -1807,13 +1896,21 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; + struct snd_soc_dai *cpu_dai; + int i; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); - else - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + /* + * Skip CPUs which don't support the current stream + * type. See soc_pcm_init_runtime_hw() for more details + */ + if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) + continue; + + dpcm_init_runtime_hw(runtime, + snd_soc_dai_get_pcm_stream(cpu_dai, + substream->stream)); + } dpcm_runtime_merge_format(substream, &runtime->hw.formats); dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min, @@ -1850,18 +1947,21 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, { struct snd_soc_dpcm *dpcm; struct snd_soc_pcm_runtime *fe = fe_substream->private_data; - struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai; + struct snd_soc_dai *fe_cpu_dai; int err; + int i; /* apply symmetry for FE */ if (soc_pcm_has_symmetry(fe_substream)) fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; - /* Symmetry only applies if we've got an active stream. */ - if (fe_cpu_dai->active) { - err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai); - if (err < 0) - return err; + for_each_rtd_cpu_dais (fe, i, fe_cpu_dai) { + /* Symmetry only applies if we've got an active stream. */ + if (fe_cpu_dai->active) { + err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai); + if (err < 0) + return err; + } } /* apply symmetry for BE */ @@ -1870,7 +1970,7 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; int i; /* A backend may not have the requested substream */ @@ -1885,17 +1985,9 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; /* Symmetry only applies if we've got an active stream. */ - if (rtd->cpu_dai->active) { - err = soc_pcm_apply_symmetry(fe_substream, - rtd->cpu_dai); - if (err < 0) - return err; - } - - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (codec_dai->active) { - err = soc_pcm_apply_symmetry(fe_substream, - codec_dai); + for_each_rtd_dais(rtd, i, dai) { + if (dai->active) { + err = soc_pcm_apply_symmetry(fe_substream, dai); if (err < 0) return err; } @@ -1913,7 +2005,7 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); - ret = dpcm_be_dai_startup(fe, fe_substream->stream); + ret = dpcm_be_dai_startup(fe, stream); if (ret < 0) { dev_err(fe->dev,"ASoC: failed to start some BEs %d\n", ret); goto be_err; @@ -1934,17 +2026,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) snd_pcm_limit_hw_rates(runtime); ret = dpcm_apply_symmetry(fe_substream, stream); - if (ret < 0) { + if (ret < 0) dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n", ret); - goto unwind; - } - - dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - return 0; unwind: - dpcm_be_dai_startup_unwind(fe, fe_substream->stream); + if (ret < 0) + dpcm_be_dai_startup_unwind(fe, stream); be_err: dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); return ret; @@ -1998,7 +2086,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); /* shutdown the BEs */ - dpcm_be_dai_shutdown(fe, substream->stream); + dpcm_be_dai_shutdown(fe, stream); dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name); @@ -2176,9 +2264,9 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); - memcpy(&fe->dpcm[substream->stream].hw_params, params, + memcpy(&fe->dpcm[stream].hw_params, params, sizeof(struct snd_pcm_hw_params)); - ret = dpcm_be_dai_hw_params(fe, substream->stream); + ret = dpcm_be_dai_hw_params(fe, stream); if (ret < 0) { dev_err(fe->dev,"ASoC: hw_params BE failed %d\n", ret); goto out; @@ -2500,7 +2588,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) goto out; } - ret = dpcm_be_dai_prepare(fe, substream->stream); + ret = dpcm_be_dai_prepare(fe, stream); if (ret < 0) goto out; @@ -2652,36 +2740,18 @@ disconnect: return ret; } -static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream) -{ - int ret; - - dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); - ret = dpcm_run_update_startup(fe, stream); - if (ret < 0) - dev_err(fe->dev, "ASoC: failed to startup some BEs\n"); - dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - - return ret; -} - -static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) -{ - int ret; - - dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); - ret = dpcm_run_update_shutdown(fe, stream); - if (ret < 0) - dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); - dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - - return ret; -} - static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) { struct snd_soc_dapm_widget_list *list; + int stream; int count, paths; + int ret; + + if (fe->num_cpus > 1) { + dev_err(fe->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } if (!fe->dai_link->dynamic) return 0; @@ -2694,74 +2764,53 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n", new ? "new" : "old", fe->dai_link->name); - /* skip if FE doesn't have playback capability */ - if (!snd_soc_dai_stream_valid(fe->cpu_dai, SNDRV_PCM_STREAM_PLAYBACK) || - !snd_soc_dai_stream_valid(fe->codec_dai, SNDRV_PCM_STREAM_PLAYBACK)) - goto capture; - - /* skip if FE isn't currently playing */ - if (!fe->cpu_dai->playback_active || !fe->codec_dai->playback_active) - goto capture; - - paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); - if (paths < 0) { - dev_warn(fe->dev, "ASoC: %s no valid %s path\n", - fe->dai_link->name, "playback"); - return paths; - } - - /* update any playback paths */ - count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, new); - if (count) { - if (new) - dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK); - else - dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK); - - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); - } + for_each_pcm_streams(stream) { - dpcm_path_put(&list); + /* skip if FE doesn't have playback/capture capability */ + if (!snd_soc_dai_stream_valid(fe->cpu_dai, stream) || + !snd_soc_dai_stream_valid(fe->codec_dai, stream)) + continue; -capture: - /* skip if FE doesn't have capture capability */ - if (!snd_soc_dai_stream_valid(fe->cpu_dai, SNDRV_PCM_STREAM_CAPTURE) || - !snd_soc_dai_stream_valid(fe->codec_dai, SNDRV_PCM_STREAM_CAPTURE)) - return 0; + /* skip if FE isn't currently playing/capturing */ + if (!fe->cpu_dai->stream_active[stream] || + !fe->codec_dai->stream_active[stream]) + continue; - /* skip if FE isn't currently capturing */ - if (!fe->cpu_dai->capture_active || !fe->codec_dai->capture_active) - return 0; + paths = dpcm_path_get(fe, stream, &list); + if (paths < 0) { + dev_warn(fe->dev, "ASoC: %s no valid %s path\n", + fe->dai_link->name, + stream == SNDRV_PCM_STREAM_PLAYBACK ? + "playback" : "capture"); + return paths; + } - paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); - if (paths < 0) { - dev_warn(fe->dev, "ASoC: %s no valid %s path\n", - fe->dai_link->name, "capture"); - return paths; - } + /* update any playback/capture paths */ + count = dpcm_process_paths(fe, stream, &list, new); + if (count) { + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); + if (new) + ret = dpcm_run_update_startup(fe, stream); + else + ret = dpcm_run_update_shutdown(fe, stream); + if (ret < 0) + dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - /* update any old capture paths */ - count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, new); - if (count) { - if (new) - dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE); - else - dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE); + dpcm_clear_pending_state(fe, stream); + dpcm_be_disconnect(fe, stream); + } - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); + dpcm_path_put(&list); } - dpcm_path_put(&list); - return 0; } /* Called by DAPM mixer/mux changes to update audio routing between PCMs and * any DAI links. */ -int soc_dpcm_runtime_update(struct snd_soc_card *card) +int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *fe; int ret = 0; @@ -2785,38 +2834,40 @@ out: mutex_unlock(&card->mutex); return ret; } -int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) +EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update); + +static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream) { + struct snd_soc_pcm_runtime *fe = fe_substream->private_data; struct snd_soc_dpcm *dpcm; - struct snd_soc_dai *dai; + int stream = fe_substream->stream; - for_each_dpcm_be(fe, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { + /* mark FE's links ready to prune */ + for_each_dpcm_be(fe, stream, dpcm) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; - struct snd_soc_pcm_runtime *be = dpcm->be; - int i; + dpcm_be_disconnect(fe, stream); - if (be->dai_link->ignore_suspend) - continue; + fe->dpcm[stream].runtime = NULL; +} - for_each_rtd_codec_dai(be, i, dai) { - struct snd_soc_dai_driver *drv = dai->driver; +static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) +{ + struct snd_soc_pcm_runtime *fe = fe_substream->private_data; + int ret; - dev_dbg(be->dev, "ASoC: BE digital mute %s\n", - be->dai_link->name); + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + ret = dpcm_fe_dai_shutdown(fe_substream); - if (drv->ops && drv->ops->digital_mute && - dai->playback_active) - drv->ops->digital_mute(dai, mute); - } - } + dpcm_fe_dai_cleanup(fe_substream); - return 0; + mutex_unlock(&fe->card->mutex); + return ret; } static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) { struct snd_soc_pcm_runtime *fe = fe_substream->private_data; - struct snd_soc_dpcm *dpcm; struct snd_soc_dapm_widget_list *list; int ret; int stream = fe_substream->stream; @@ -2826,8 +2877,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) ret = dpcm_path_get(fe, stream, &list); if (ret < 0) { - mutex_unlock(&fe->card->mutex); - return ret; + goto open_end; } else if (ret == 0) { dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", fe->dai_link->name, stream ? "capture" : "playback"); @@ -2837,37 +2887,12 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) dpcm_process_paths(fe, stream, &list, 1); ret = dpcm_fe_dai_startup(fe_substream); - if (ret < 0) { - /* clean up all links */ - for_each_dpcm_be(fe, stream, dpcm) - dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; - - dpcm_be_disconnect(fe, stream); - fe->dpcm[stream].runtime = NULL; - } + if (ret < 0) + dpcm_fe_dai_cleanup(fe_substream); dpcm_clear_pending_state(fe, stream); dpcm_path_put(&list); - mutex_unlock(&fe->card->mutex); - return ret; -} - -static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) -{ - struct snd_soc_pcm_runtime *fe = fe_substream->private_data; - struct snd_soc_dpcm *dpcm; - int stream = fe_substream->stream, ret; - - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); - ret = dpcm_fe_dai_shutdown(fe_substream); - - /* mark FE's links ready to prune */ - for_each_dpcm_be(fe, stream, dpcm) - dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; - - dpcm_be_disconnect(fe, stream); - - fe->dpcm[stream].runtime = NULL; +open_end: mutex_unlock(&fe->card->mutex); return ret; } @@ -2876,7 +2901,7 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_dai *codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_component *component; struct snd_pcm *pcm; char new_name[64]; @@ -2888,22 +2913,29 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) capture = rtd->dai_link->dpcm_capture; } else { /* Adapt stream for codec2codec links */ - struct snd_soc_pcm_stream *cpu_capture = rtd->dai_link->params ? - &cpu_dai->driver->playback : &cpu_dai->driver->capture; - struct snd_soc_pcm_stream *cpu_playback = rtd->dai_link->params ? - &cpu_dai->driver->capture : &cpu_dai->driver->playback; + int cpu_capture = rtd->dai_link->params ? + SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; + int cpu_playback = rtd->dai_link->params ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (rtd->num_cpus == 1) { + cpu_dai = rtd->cpu_dais[0]; + } else if (rtd->num_cpus == rtd->num_codecs) { + cpu_dai = rtd->cpu_dais[i]; + } else { + dev_err(rtd->card->dev, + "N cpus to M codecs link is not supported yet\n"); + return -EINVAL; + } - for_each_rtd_codec_dai(rtd, i, codec_dai) { if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && - snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) + snd_soc_dai_stream_valid(cpu_dai, cpu_playback)) playback = 1; if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && - snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) + snd_soc_dai_stream_valid(cpu_dai, cpu_capture)) capture = 1; } - - capture = capture && cpu_capture->channels_min; - playback = playback && cpu_playback->channels_min; } if (rtd->dai_link->playback_only) { @@ -3017,7 +3049,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) out: dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, - cpu_dai->name); + (rtd->num_cpus > 1) ? "multicpu" : rtd->cpu_dai->name); return ret; } @@ -3050,33 +3082,17 @@ struct snd_pcm_substream * } EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream); -/* get the BE runtime state */ -enum snd_soc_dpcm_state - snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream) -{ - return be->dpcm[stream].state; -} -EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state); - -/* set the BE runtime state */ -void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, - int stream, enum snd_soc_dpcm_state state) -{ - be->dpcm[stream].state = state; -} -EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state); - -/* - * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE - * are not running, paused or suspended for the specified stream direction. - */ -int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, - struct snd_soc_pcm_runtime *be, int stream) +static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, + int stream, + const enum snd_soc_dpcm_state *states, + int num_states) { 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) { @@ -3085,18 +3101,34 @@ int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, continue; state = dpcm->fe->dpcm[stream].state; - if (state == SND_SOC_DPCM_STATE_START || - state == SND_SOC_DPCM_STATE_PAUSED || - state == SND_SOC_DPCM_STATE_SUSPEND) { - ret = 0; - break; + for (i = 0; i < num_states; i++) { + if (state == states[i]) { + ret = 0; + break; + } } } spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); - /* it's safe to free/stop this BE DAI */ + /* it's safe to do this BE DAI */ return ret; } + +/* + * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE + * are not running, paused or suspended for the specified stream direction. + */ +int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) +{ + const enum snd_soc_dpcm_state state[] = { + SND_SOC_DPCM_STATE_START, + SND_SOC_DPCM_STATE_PAUSED, + SND_SOC_DPCM_STATE_SUSPEND, + }; + + return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); +} EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop); /* @@ -3106,168 +3138,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop); int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream) { - struct snd_soc_dpcm *dpcm; - int state; - int ret = 1; - unsigned long flags; - - spin_lock_irqsave(&fe->card->dpcm_lock, flags); - for_each_dpcm_fe(be, stream, dpcm) { - - if (dpcm->fe == fe) - continue; + const enum snd_soc_dpcm_state state[] = { + SND_SOC_DPCM_STATE_START, + SND_SOC_DPCM_STATE_PAUSED, + SND_SOC_DPCM_STATE_SUSPEND, + SND_SOC_DPCM_STATE_PREPARE, + }; - state = dpcm->fe->dpcm[stream].state; - if (state == SND_SOC_DPCM_STATE_START || - state == SND_SOC_DPCM_STATE_PAUSED || - state == SND_SOC_DPCM_STATE_SUSPEND || - state == SND_SOC_DPCM_STATE_PREPARE) { - ret = 0; - break; - } - } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); - - /* it's safe to change hw_params */ - return ret; + return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); } EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); - -#ifdef CONFIG_DEBUG_FS -static const char *dpcm_state_string(enum snd_soc_dpcm_state state) -{ - switch (state) { - case SND_SOC_DPCM_STATE_NEW: - return "new"; - case SND_SOC_DPCM_STATE_OPEN: - return "open"; - case SND_SOC_DPCM_STATE_HW_PARAMS: - return "hw_params"; - case SND_SOC_DPCM_STATE_PREPARE: - return "prepare"; - case SND_SOC_DPCM_STATE_START: - return "start"; - case SND_SOC_DPCM_STATE_STOP: - return "stop"; - case SND_SOC_DPCM_STATE_SUSPEND: - return "suspend"; - case SND_SOC_DPCM_STATE_PAUSED: - return "paused"; - case SND_SOC_DPCM_STATE_HW_FREE: - return "hw_free"; - case SND_SOC_DPCM_STATE_CLOSE: - return "close"; - } - - return "unknown"; -} - -static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, - int stream, char *buf, size_t size) -{ - 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, - "[%s - %s]\n", fe->dai_link->name, - stream ? "Capture" : "Playback"); - - offset += scnprintf(buf + offset, size - offset, "State: %s\n", - dpcm_state_string(fe->dpcm[stream].state)); - - if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && - (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) - offset += scnprintf(buf + offset, size - offset, - "Hardware Params: " - "Format = %s, Channels = %d, Rate = %d\n", - snd_pcm_format_name(params_format(params)), - params_channels(params), - params_rate(params)); - - /* BEs state */ - offset += scnprintf(buf + offset, size - offset, "Backends:\n"); - - if (list_empty(&fe->dpcm[stream].be_clients)) { - offset += scnprintf(buf + offset, size - offset, - " No active DSP links\n"); - 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; - - offset += scnprintf(buf + offset, size - offset, - "- %s\n", be->dai_link->name); - - offset += scnprintf(buf + offset, size - offset, - " State: %s\n", - dpcm_state_string(be->dpcm[stream].state)); - - if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && - (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) - offset += scnprintf(buf + offset, size - offset, - " Hardware Params: " - "Format = %s, Channels = %d, Rate = %d\n", - snd_pcm_format_name(params_format(params)), - params_channels(params), - params_rate(params)); - } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); -out: - return offset; -} - -static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct snd_soc_pcm_runtime *fe = file->private_data; - ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0; - char *buf; - - buf = kmalloc(out_count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (snd_soc_dai_stream_valid(fe->cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) - offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK, - buf + offset, out_count - offset); - - if (snd_soc_dai_stream_valid(fe->cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) - offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE, - buf + offset, out_count - offset); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); - - kfree(buf); - return ret; -} - -static const struct file_operations dpcm_state_fops = { - .open = simple_open, - .read = dpcm_state_read_file, - .llseek = default_llseek, -}; - -void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) -{ - if (!rtd->dai_link) - return; - - if (!rtd->dai_link->dynamic) - return; - - if (!rtd->card->debugfs_card_root) - return; - - rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name, - rtd->card->debugfs_card_root); - - debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root, - rtd, &dpcm_state_fops); -} -#endif diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 575da6aba807..1f81cd2d29cf 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -251,7 +251,7 @@ static int soc_tplg_vendor_load_(struct soc_tplg *tplg, { int ret = 0; - if (tplg->comp && tplg->ops && tplg->ops->vendor_load) + if (tplg->ops && tplg->ops->vendor_load) ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr); else { dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", @@ -283,7 +283,7 @@ static int soc_tplg_vendor_load(struct soc_tplg *tplg, static int soc_tplg_widget_load(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { - if (tplg->comp && tplg->ops && tplg->ops->widget_load) + if (tplg->ops && tplg->ops->widget_load) return tplg->ops->widget_load(tplg->comp, tplg->index, w, tplg_w); @@ -295,7 +295,7 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, static int soc_tplg_widget_ready(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { - if (tplg->comp && tplg->ops && tplg->ops->widget_ready) + if (tplg->ops && tplg->ops->widget_ready) return tplg->ops->widget_ready(tplg->comp, tplg->index, w, tplg_w); @@ -307,7 +307,7 @@ static int soc_tplg_dai_load(struct soc_tplg *tplg, struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - if (tplg->comp && tplg->ops && tplg->ops->dai_load) + if (tplg->ops && tplg->ops->dai_load) return tplg->ops->dai_load(tplg->comp, tplg->index, dai_drv, pcm, dai); @@ -318,7 +318,7 @@ static int soc_tplg_dai_load(struct soc_tplg *tplg, static int soc_tplg_dai_link_load(struct soc_tplg *tplg, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { - if (tplg->comp && tplg->ops && tplg->ops->link_load) + if (tplg->ops && tplg->ops->link_load) return tplg->ops->link_load(tplg->comp, tplg->index, link, cfg); return 0; @@ -327,7 +327,7 @@ static int soc_tplg_dai_link_load(struct soc_tplg *tplg, /* tell the component driver that all firmware has been loaded in this request */ static void soc_tplg_complete(struct soc_tplg *tplg) { - if (tplg->comp && tplg->ops && tplg->ops->complete) + if (tplg->ops && tplg->ops->complete) tplg->ops->complete(tplg->comp); } @@ -684,7 +684,7 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event); static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) { - if (tplg->comp && tplg->ops && tplg->ops->control_load) + if (tplg->ops && tplg->ops->control_load) return tplg->ops->control_load(tplg->comp, tplg->index, k, hdr); @@ -1174,7 +1174,7 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, static int soc_tplg_add_route(struct soc_tplg *tplg, struct snd_soc_dapm_route *route) { - if (tplg->comp && tplg->ops && tplg->ops->dapm_route_load) + if (tplg->ops && tplg->ops->dapm_route_load) return tplg->ops->dapm_route_load(tplg->comp, tplg->index, route); @@ -2564,7 +2564,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, } /* pass control to component driver for optional further init */ - if (tplg->comp && tplg->ops && tplg->ops->manifest) + if (tplg->ops && tplg->ops->manifest) ret = tplg->ops->manifest(tplg->comp, tplg->index, _manifest); if (!abi_match) /* free the duplicated one */ @@ -2736,6 +2736,10 @@ int snd_soc_tplg_component_load(struct snd_soc_component *comp, struct soc_tplg tplg; int ret; + /* component needs to exist to keep and reference data while parsing */ + if (!comp) + return -EINVAL; + /* setup parsing context */ memset(&tplg, 0, sizeof(tplg)); tplg.fw = fw; @@ -2774,7 +2778,7 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w, *next_w; - list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { + for_each_card_widgets_safe(dapm->card, w, next_w) { /* make sure we are a widget with correct context */ if (w->dobj.type != SND_SOC_DOBJ_WIDGET || w->dapm != dapm) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 827b0ec92522..4dda4b62509f 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -41,6 +41,15 @@ config SND_SOC_SOF_OF required to enable i.MX8 devices. Say Y if you need this option. If unsure select "N". +config SND_SOC_SOF_DEBUG_PROBES + bool "SOF enable data probing" + select SND_SOC_COMPRESS + help + This option enables the data probing feature that can be used to + gather data directly from specific points of the audio pipeline. + Say Y if you want to enable probes. + If unsure, select "N". + config SND_SOC_SOF_DEVELOPER_SUPPORT bool "SOF developer options support" depends on EXPERT diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 0a8bc72c28a5..8eca2f85c90e 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,6 +2,7 @@ snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o utils.o sof-audio.o +snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += probe.o compress.o snd-sof-pci-objs := sof-pci-dev.o snd-sof-acpi-objs := sof-acpi-dev.o diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c new file mode 100644 index 000000000000..7354dc6a49cf --- /dev/null +++ b/sound/soc/sof/compress.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: (GPL-2.0 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) 2019-2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski <cezary.rojewski@intel.com> +// + +#include <sound/soc.h> +#include "compress.h" +#include "ops.h" +#include "probe.h" + +struct snd_compr_ops sof_probe_compressed_ops = { + .copy = sof_probe_compr_copy, +}; +EXPORT_SYMBOL(sof_probe_compressed_ops); + +int sof_probe_compr_open(struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(dai->component); + int ret; + + ret = snd_sof_probe_compr_assign(sdev, cstream, dai); + if (ret < 0) { + dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret); + return ret; + } + + sdev->extractor_stream_tag = ret; + return 0; +} +EXPORT_SYMBOL(sof_probe_compr_open); + +int sof_probe_compr_free(struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(dai->component); + struct sof_probe_point_desc *desc; + size_t num_desc; + int i, ret; + + /* disconnect all probe points */ + ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); + if (ret < 0) { + dev_err(dai->dev, "Failed to get probe points: %d\n", ret); + goto exit; + } + + for (i = 0; i < num_desc; i++) + sof_ipc_probe_points_remove(sdev, &desc[i].buffer_id, 1); + kfree(desc); + +exit: + ret = sof_ipc_probe_deinit(sdev); + if (ret < 0) + dev_err(dai->dev, "Failed to deinit probe: %d\n", ret); + + sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; + snd_compr_free_pages(cstream); + + return snd_sof_probe_compr_free(sdev, cstream, dai); +} +EXPORT_SYMBOL(sof_probe_compr_free); + +int sof_probe_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params, struct snd_soc_dai *dai) +{ + struct snd_compr_runtime *rtd = cstream->runtime; + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(dai->component); + int ret; + + cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + cstream->dma_buffer.dev.dev = sdev->dev; + ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); + if (ret < 0) + return ret; + + ret = snd_sof_probe_compr_set_params(sdev, cstream, params, dai); + if (ret < 0) + return ret; + + ret = sof_ipc_probe_init(sdev, sdev->extractor_stream_tag, + rtd->dma_bytes); + if (ret < 0) { + dev_err(dai->dev, "Failed to init probe: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(sof_probe_compr_set_params); + +int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(dai->component); + + return snd_sof_probe_compr_trigger(sdev, cstream, cmd, dai); +} +EXPORT_SYMBOL(sof_probe_compr_trigger); + +int sof_probe_compr_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(dai->component); + + return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai); +} +EXPORT_SYMBOL(sof_probe_compr_pointer); + +int sof_probe_compr_copy(struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *rtd = cstream->runtime; + unsigned int offset, n; + void *ptr; + int ret; + + if (count > rtd->buffer_size) + count = rtd->buffer_size; + + div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset); + ptr = rtd->dma_area + offset; + n = rtd->buffer_size - offset; + + if (count < n) { + ret = copy_to_user(buf, ptr, count); + } else { + ret = copy_to_user(buf, ptr, n); + ret += copy_to_user(buf + n, rtd->dma_area, count - n); + } + + if (ret) + return count - ret; + return count; +} +EXPORT_SYMBOL(sof_probe_compr_copy); diff --git a/sound/soc/sof/compress.h b/sound/soc/sof/compress.h new file mode 100644 index 000000000000..800f163603e1 --- /dev/null +++ b/sound/soc/sof/compress.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: (GPL-2.0 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) 2019-2020 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski <cezary.rojewski@intel.com> + */ + +#ifndef __SOF_COMPRESS_H +#define __SOF_COMPRESS_H + +#include <sound/compress_driver.h> + +extern struct snd_compr_ops sof_probe_compressed_ops; + +int sof_probe_compr_open(struct snd_compr_stream *cstream, + struct snd_soc_dai *dai); +int sof_probe_compr_free(struct snd_compr_stream *cstream, + struct snd_soc_dai *dai); +int sof_probe_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params, struct snd_soc_dai *dai); +int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai); +int sof_probe_compr_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai); +int sof_probe_compr_copy(struct snd_compr_stream *cstream, + char __user *buf, size_t count); + +#endif diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 34cefbaf2d2a..91acfae7935c 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -14,6 +14,9 @@ #include <sound/sof.h> #include "sof-priv.h" #include "ops.h" +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) +#include "probe.h" +#endif /* see SOF_DBG_ flags */ int sof_core_debug; @@ -286,12 +289,15 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) /* initialize sof device */ sdev->dev = dev; - /* initialize default D0 sub-state */ - sdev->d0_substate = SOF_DSP_D0I0; + /* initialize default DSP power state */ + sdev->dsp_power_state.state = SOF_DSP_PM_D0; sdev->pdata = plat_data; sdev->first_boot = true; sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) + sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; +#endif dev_set_drvdata(dev, sdev); /* check all mandatory ops */ diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index d2b3b99d3a20..b5c0d6cf72cc 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -17,6 +17,221 @@ #include "sof-priv.h" #include "ops.h" +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) +#include "probe.h" + +/** + * strsplit_u32 - Split string into sequence of u32 tokens + * @buf: String to split into tokens. + * @delim: String containing delimiter characters. + * @tkns: Returned u32 sequence pointer. + * @num_tkns: Returned number of tokens obtained. + */ +static int +strsplit_u32(char **buf, const char *delim, u32 **tkns, size_t *num_tkns) +{ + char *s; + u32 *data, *tmp; + size_t count = 0; + size_t cap = 32; + int ret = 0; + + *tkns = NULL; + *num_tkns = 0; + data = kcalloc(cap, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + while ((s = strsep(buf, delim)) != NULL) { + ret = kstrtouint(s, 0, data + count); + if (ret) + goto exit; + if (++count >= cap) { + cap *= 2; + tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto exit; + } + data = tmp; + } + } + + if (!count) + goto exit; + *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL); + if (*tkns == NULL) { + ret = -ENOMEM; + goto exit; + } + *num_tkns = count; + +exit: + kfree(data); + return ret; +} + +static int tokenize_input(const char __user *from, size_t count, + loff_t *ppos, u32 **tkns, size_t *num_tkns) +{ + char *buf; + int ret; + + buf = kmalloc(count + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = simple_write_to_buffer(buf, count, ppos, from, count); + if (ret != count) { + ret = ret >= 0 ? -EIO : ret; + goto exit; + } + + buf[count] = '\0'; + ret = strsplit_u32((char **)&buf, ",", tkns, num_tkns); +exit: + kfree(buf); + return ret; +} + +static ssize_t probe_points_read(struct file *file, + char __user *to, size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + struct sof_probe_point_desc *desc; + size_t num_desc, len = 0; + char *buf; + int i, ret; + + if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { + dev_warn(sdev->dev, "no extractor stream running\n"); + return -ENOENT; + } + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); + if (ret < 0) + goto exit; + + for (i = 0; i < num_desc; i++) { + ret = snprintf(buf + len, PAGE_SIZE - len, + "Id: %#010x Purpose: %d Node id: %#x\n", + desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag); + if (ret < 0) + goto free_desc; + len += ret; + } + + ret = simple_read_from_buffer(to, count, ppos, buf, len); +free_desc: + kfree(desc); +exit: + kfree(buf); + return ret; +} + +static ssize_t probe_points_write(struct file *file, + const char __user *from, size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + struct sof_probe_point_desc *desc; + size_t num_tkns, bytes; + u32 *tkns; + int ret; + + if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { + dev_warn(sdev->dev, "no extractor stream running\n"); + return -ENOENT; + } + + ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); + if (ret < 0) + return ret; + bytes = sizeof(*tkns) * num_tkns; + if (!num_tkns || (bytes % sizeof(*desc))) { + ret = -EINVAL; + goto exit; + } + + desc = (struct sof_probe_point_desc *)tkns; + ret = sof_ipc_probe_points_add(sdev, + desc, bytes / sizeof(*desc)); + if (!ret) + ret = count; +exit: + kfree(tkns); + return ret; +} + +static const struct file_operations probe_points_fops = { + .open = simple_open, + .read = probe_points_read, + .write = probe_points_write, + .llseek = default_llseek, +}; + +static ssize_t probe_points_remove_write(struct file *file, + const char __user *from, size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + size_t num_tkns; + u32 *tkns; + int ret; + + if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { + dev_warn(sdev->dev, "no extractor stream running\n"); + return -ENOENT; + } + + ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); + if (ret < 0) + return ret; + if (!num_tkns) { + ret = -EINVAL; + goto exit; + } + + ret = sof_ipc_probe_points_remove(sdev, tkns, num_tkns); + if (!ret) + ret = count; +exit: + kfree(tkns); + return ret; +} + +static const struct file_operations probe_points_remove_fops = { + .open = simple_open, + .write = probe_points_remove_write, + .llseek = default_llseek, +}; + +static int snd_sof_debugfs_probe_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; + + 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 + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) #define MAX_IPC_FLOOD_DURATION_MS 1000 #define MAX_IPC_FLOOD_COUNT 10000 @@ -436,6 +651,17 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) return err; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) + err = snd_sof_debugfs_probe_item(sdev, "probe_points", + 0644, &probe_points_fops); + if (err < 0) + return err; + err = snd_sof_debugfs_probe_item(sdev, "probe_points_remove", + 0200, &probe_points_remove_fops); + if (err < 0) + return err; +#endif + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) /* create read-write ipc_flood_count debugfs entry */ err = snd_sof_debugfs_buf_item(sdev, NULL, 0, diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index b2556f5e2871..b692752b2178 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -138,7 +138,7 @@ static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) /* * DSP control. */ -static int imx8_run(struct snd_sof_dev *sdev) +static int imx8x_run(struct snd_sof_dev *sdev) { struct imx8_priv *dsp_priv = (struct imx8_priv *)sdev->private; int ret; @@ -178,6 +178,24 @@ static int imx8_run(struct snd_sof_dev *sdev) return 0; } +static int imx8_run(struct snd_sof_dev *sdev) +{ + struct imx8_priv *dsp_priv = (struct imx8_priv *)sdev->private; + int ret; + + ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP, + IMX_SC_C_OFS_SEL, 0); + if (ret < 0) { + dev_err(sdev->dev, "Error system address offset source select\n"); + return ret; + } + + imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true, + RESET_VECTOR_VADDR); + + return 0; +} + static int imx8_probe(struct snd_sof_dev *sdev) { struct platform_device *pdev = @@ -360,7 +378,7 @@ static struct snd_soc_dai_driver imx8_dai[] = { }, }; -/* i.MX8 ops */ +/* i.MX8 ops */ struct snd_sof_dsp_ops sof_imx8_ops = { /* probe and remove */ .probe = imx8_probe, @@ -390,6 +408,39 @@ struct snd_sof_dsp_ops sof_imx8_ops = { /* DAI drivers */ .drv = imx8_dai, .num_drv = 1, /* we have only 1 ESAI interface on i.MX8 */ +}; +EXPORT_SYMBOL(sof_imx8_ops); + +/* i.MX8X ops */ +struct snd_sof_dsp_ops sof_imx8x_ops = { + /* probe and remove */ + .probe = imx8_probe, + .remove = imx8_remove, + /* DSP core boot */ + .run = imx8x_run, + + /* Block IO */ + .block_read = sof_block_read, + .block_write = sof_block_write, + + /* ipc */ + .send_msg = imx8_send_msg, + .fw_ready = sof_fw_ready, + .get_mailbox_offset = imx8_get_mailbox_offset, + .get_window_offset = imx8_get_window_offset, + + .ipc_msg_data = imx8_ipc_msg_data, + .ipc_pcm_params = imx8_ipc_pcm_params, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + .get_bar_index = imx8_get_bar_index, + /* firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + + /* DAI drivers */ + .drv = imx8_dai, + .num_drv = 1, /* we have only 1 ESAI interface on i.MX8 */ /* ALSA HW info flags */ .hw_info = SNDRV_PCM_INFO_MMAP | @@ -398,6 +449,6 @@ struct snd_sof_dsp_ops sof_imx8_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP }; -EXPORT_SYMBOL(sof_imx8_ops); +EXPORT_SYMBOL(sof_imx8x_ops); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 56a837d2cb95..c9a2bee4b55c 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -305,6 +305,15 @@ config SND_SOC_SOF_HDA_AUDIO_CODEC Say Y if you want to enable HDAudio codecs with SOF. If unsure select "N". +config SND_SOC_SOF_HDA_PROBES + bool "SOF enable probes over HDA" + depends on SND_SOC_SOF_DEBUG_PROBES + help + This option enables the data probing for Intel(R). + Intel(R) Skylake and newer platforms. + Say Y if you want to enable probes. + If unsure, select "N". + config SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1 bool "SOF enable DMI Link L1" help @@ -315,17 +324,6 @@ config SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1 Say Y if you want to enable DMI Link L1 If unsure, select "N". -config SND_SOC_SOF_HDA_COMMON_HDMI_CODEC - bool "SOF common HDA HDMI codec driver" - depends on SND_SOC_SOF_HDA_LINK - depends on SND_HDA_CODEC_HDMI - default SND_HDA_CODEC_HDMI - help - This adds support for HDMI audio by using the common HDA - HDMI/DisplayPort codec driver. - Say Y if you want to use the common codec driver with SOF. - If unsure select "Y". - endif ## SND_SOC_SOF_HDA_COMMON config SND_SOC_SOF_HDA_LINK_BASELINE diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index b8f58e006e29..cee02a2e00f4 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -9,6 +9,7 @@ snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ hda-dai.o hda-bus.o \ apl.o cnl.o +snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-compress.o snd-sof-intel-hda-objs := hda-codec.o diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 2483b15699e7..02218d22e51f 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -73,6 +73,15 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) + /* probe callbacks */ + .probe_assign = hda_probe_compr_assign, + .probe_free = hda_probe_compr_free, + .probe_set_params = hda_probe_compr_set_params, + .probe_trigger = hda_probe_compr_trigger, + .probe_pointer = hda_probe_compr_pointer, +#endif + /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 9e2d8afe0535..e427d00eca71 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -65,11 +65,6 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) hda_dsp_ipc_get_reply(sdev); snd_sof_ipc_reply(sdev, msg); - if (sdev->code_loading) { - sdev->code_loading = 0; - wake_up(&sdev->waitq); - } - cnl_ipc_dsp_done(sdev); spin_unlock_irq(&sdev->ipc_lock); @@ -171,23 +166,48 @@ static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg, static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + struct sof_ipc_cmd_hdr *hdr; u32 dr = 0; u32 dd = 0; + /* + * Currently the only compact IPC supported is the PM_GATE + * IPC which is used for transitioning the DSP between the + * D0I0 and D0I3 states. And these are sent only during the + * set_power_state() op. Therefore, there will never be a case + * that a compact IPC results in the DSP exiting D0I3 without + * the host and FW being in sync. + */ if (cnl_compact_ipc_compress(msg, &dr, &dd)) { /* send the message via IPC registers */ snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD, dd); snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, CNL_DSP_REG_HIPCIDR_BUSY | dr); - } else { - /* send the message via mailbox */ - sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, - msg->msg_size); - snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, - CNL_DSP_REG_HIPCIDR_BUSY); + return 0; } + /* send the message via mailbox */ + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, + CNL_DSP_REG_HIPCIDR_BUSY); + + hdr = msg->msg_data; + + /* + * Use mod_delayed_work() to schedule the delayed work + * to avoid scheduling multiple workqueue items when + * IPCs are sent at a high-rate. mod_delayed_work() + * modifies the timer if the work is pending. + * Also, a new delayed work should not be queued after the + * the CTX_SAVE IPC, which is sent before the DSP enters D3. + */ + if (hdr->cmd != (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) + mod_delayed_work(system_wq, &hdev->d0i3_work, + msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS)); + return 0; } @@ -259,6 +279,15 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) + /* probe callbacks */ + .probe_assign = hda_probe_compr_assign, + .probe_free = hda_probe_compr_free, + .probe_set_params = hda_probe_compr_set_params, + .probe_trigger = hda_probe_compr_trigger, + .probe_pointer = hda_probe_compr_pointer, +#endif + /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index ff45075ef720..3041fbbb010a 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -113,8 +113,14 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address, if (ret < 0) return ret; - if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) + if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) { + if (!hdev->bus->audio_component) { + dev_dbg(sdev->dev, + "iDisp hw present but no driver\n"); + return -ENOENT; + } hda_priv->need_display_power = true; + } /* * if common HDMI codec driver is not used, codec load @@ -203,6 +209,9 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev) struct hdac_bus *bus = sof_to_bus(sdev); int ret; + if (!bus->audio_component) + return 0; + /* power down unconditionally */ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); diff --git a/sound/soc/sof/intel/hda-compress.c b/sound/soc/sof/intel/hda-compress.c new file mode 100644 index 000000000000..38a1ebec8478 --- /dev/null +++ b/sound/soc/sof/intel/hda-compress.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: (GPL-2.0 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) 2019-2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski <cezary.rojewski@intel.com> +// + +#include <sound/hdaudio_ext.h> +#include <sound/soc.h> +#include "../sof-priv.h" +#include "hda.h" + +static inline struct hdac_ext_stream * +hda_compr_get_stream(struct snd_compr_stream *cstream) +{ + return cstream->runtime->private_data; +} + +int hda_probe_compr_assign(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream; + + stream = hda_dsp_stream_get(sdev, cstream->direction); + if (!stream) + return -EBUSY; + + hdac_stream(stream)->curr_pos = 0; + hdac_stream(stream)->cstream = cstream; + cstream->runtime->private_data = stream; + + return hdac_stream(stream)->stream_tag; +} + +int hda_probe_compr_free(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + int ret; + + ret = hda_dsp_stream_put(sdev, cstream->direction, + hdac_stream(stream)->stream_tag); + if (ret < 0) { + dev_dbg(sdev->dev, "stream put failed: %d\n", ret); + return ret; + } + + hdac_stream(stream)->cstream = NULL; + cstream->runtime->private_data = NULL; + + return 0; +} + +int hda_probe_compr_set_params(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + struct hdac_stream *hstream = hdac_stream(stream); + struct snd_dma_buffer *dmab; + u32 bits, rate; + int bps, ret; + + dmab = cstream->runtime->dma_buffer_p; + /* compr params do not store bit depth, default to S32_LE */ + bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE); + if (bps < 0) + return bps; + bits = hda_dsp_get_bits(sdev, bps); + rate = hda_dsp_get_mult_div(sdev, params->codec.sample_rate); + + hstream->format_val = rate | bits | (params->codec.ch_out - 1); + hstream->bufsize = cstream->runtime->buffer_size; + hstream->period_bytes = cstream->runtime->fragment_size; + hstream->no_period_wakeup = 0; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + return ret; + } + + return 0; +} + +int hda_probe_compr_trigger(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + + return hda_dsp_stream_trigger(sdev, stream, cmd); +} + +int hda_probe_compr_pointer(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + struct snd_soc_pcm_stream *pstream; + + pstream = &dai->driver->capture; + tstamp->copied_total = hdac_stream(stream)->curr_pos; + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates); + + return 0; +} diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index 871b71a15a63..6288b2f99540 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> +#include <sound/hda_component.h> #include "../ops.h" #include "hda.h" @@ -64,15 +65,32 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) struct hdac_bus *bus = sof_to_bus(sdev); u32 cap, offset, feature; int count = 0; + int ret; + + /* + * On some devices, one reset cycle is necessary before reading + * capabilities + */ + ret = hda_dsp_ctrl_link_reset(sdev, true); + if (ret < 0) + return ret; + ret = hda_dsp_ctrl_link_reset(sdev, false); + if (ret < 0) + return ret; offset = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_LLCH); do { - cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); - dev_dbg(sdev->dev, "checking for capabilities at offset 0x%x\n", offset & SOF_HDA_CAP_NEXT_MASK); + cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); + + if (cap == -1) { + dev_dbg(bus->dev, "Invalid capability reg read\n"); + break; + } + feature = (cap & SOF_HDA_CAP_ID_MASK) >> SOF_HDA_CAP_ID_OFF; switch (feature) { @@ -105,8 +123,8 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) bus->mlcap = bus->remap_addr + offset; break; default: - dev_vdbg(sdev->dev, "found capability %d at 0x%x\n", - feature, offset); + dev_dbg(sdev->dev, "found capability %d at 0x%x\n", + feature, offset); break; } @@ -176,6 +194,9 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) if (bus->chip_init) return 0; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + snd_hdac_set_codec_wakeup(bus, true); +#endif hda_dsp_ctrl_misc_clock_gating(sdev, false); if (full_reset) { @@ -183,7 +204,7 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) ret = hda_dsp_ctrl_link_reset(sdev, true); if (ret < 0) { dev_err(sdev->dev, "error: failed to reset HDA controller\n"); - return ret; + goto err; } usleep_range(500, 1000); @@ -192,7 +213,7 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) ret = hda_dsp_ctrl_link_reset(sdev, false); if (ret < 0) { dev_err(sdev->dev, "error: failed to exit HDA controller reset\n"); - return ret; + goto err; } usleep_range(1000, 1200); @@ -202,7 +223,8 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) /* check to see if controller is ready */ if (!snd_hdac_chip_readb(bus, GCTL)) { dev_dbg(bus->dev, "controller not ready!\n"); - return -EBUSY; + ret = -EBUSY; + goto err; } /* Accept unsolicited responses */ @@ -268,7 +290,11 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) bus->chip_init = true; +err: hda_dsp_ctrl_misc_clock_gating(sdev, true); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + snd_hdac_set_codec_wakeup(bus, false); +#endif return ret; } diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 9c6e3f990ee3..833dc303b394 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -204,7 +204,7 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, struct hdac_bus *bus = hstream->bus; struct hdac_ext_stream *link_dev; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct sof_intel_hda_stream *hda_stream; struct hda_pipe_params p_params = {0}; struct hdac_ext_link *link; @@ -293,7 +293,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, bus = hstream->bus; rtd = snd_pcm_substream_chip(substream); - link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); + link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name); if (!link) return -EINVAL; @@ -374,7 +374,7 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream, if (ret < 0) return ret; - link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); + link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name); if (!link) return -EINVAL; @@ -399,6 +399,19 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = { .trigger = hda_link_pcm_trigger, .prepare = hda_link_pcm_prepare, }; + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) +#include "../compress.h" + +static struct snd_soc_cdai_ops sof_probe_compr_ops = { + .startup = sof_probe_compr_open, + .shutdown = sof_probe_compr_free, + .set_params = sof_probe_compr_set_params, + .trigger = sof_probe_compr_trigger, + .pointer = sof_probe_compr_pointer, +}; + +#endif #endif /* @@ -409,56 +422,167 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = { struct snd_soc_dai_driver skl_dai[] = { { .name = "SSP0 Pin", + .playback = { + .channels_min = 1, + .channels_max = 8, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "SSP1 Pin", + .playback = { + .channels_min = 1, + .channels_max = 8, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "SSP2 Pin", + .playback = { + .channels_min = 1, + .channels_max = 8, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "SSP3 Pin", + .playback = { + .channels_min = 1, + .channels_max = 8, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "SSP4 Pin", + .playback = { + .channels_min = 1, + .channels_max = 8, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "SSP5 Pin", + .playback = { + .channels_min = 1, + .channels_max = 8, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "DMIC01 Pin", + .capture = { + .channels_min = 1, + .channels_max = 4, + }, }, { .name = "DMIC16k Pin", + .capture = { + .channels_min = 1, + .channels_max = 4, + }, }, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) { .name = "iDisp1 Pin", .ops = &hda_link_dai_ops, + .playback = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "iDisp2 Pin", .ops = &hda_link_dai_ops, + .playback = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "iDisp3 Pin", .ops = &hda_link_dai_ops, + .playback = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "iDisp4 Pin", .ops = &hda_link_dai_ops, + .playback = { + .channels_min = 1, + .channels_max = 8, + }, }, { .name = "Analog CPU DAI", .ops = &hda_link_dai_ops, + .playback = { + .channels_min = 1, + .channels_max = 16, + }, + .capture = { + .channels_min = 1, + .channels_max = 16, + }, }, { .name = "Digital CPU DAI", .ops = &hda_link_dai_ops, + .playback = { + .channels_min = 1, + .channels_max = 16, + }, + .capture = { + .channels_min = 1, + .channels_max = 16, + }, }, { .name = "Alt Analog CPU DAI", .ops = &hda_link_dai_ops, + .playback = { + .channels_min = 1, + .channels_max = 16, + }, + .capture = { + .channels_min = 1, + .channels_max = 16, + }, +}, +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) +{ + .name = "Probe Extraction CPU DAI", + .compress_new = snd_soc_new_compress, + .cops = &sof_probe_compr_ops, + .capture = { + .stream_name = "Probe Extraction", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + }, }, #endif +#endif }; diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 0848b79967a9..99087b6afb67 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -15,12 +15,21 @@ * Hardware interface for generic Intel audio DSP HDA IP */ +#include <linux/module.h> #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> +#include "../sof-audio.h" #include "../ops.h" #include "hda.h" #include "hda-ipc.h" +static bool hda_enable_trace_D0I3_S0; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG) +module_param_named(enable_trace_D0I3_S0, hda_enable_trace_D0I3_S0, bool, 0444); +MODULE_PARM_DESC(enable_trace_D0I3_S0, + "SOF HDA enable trace when the DSP is in D0I3 in S0"); +#endif + /* * DSP Core control. */ @@ -334,17 +343,15 @@ static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags) pm_gate.flags = flags; /* send pm_gate ipc to dsp */ - return sof_ipc_tx_message(sdev->ipc, pm_gate.hdr.cmd, &pm_gate, - sizeof(pm_gate), &reply, sizeof(reply)); + return sof_ipc_tx_message_no_pm(sdev->ipc, pm_gate.hdr.cmd, + &pm_gate, sizeof(pm_gate), &reply, + sizeof(reply)); } -int hda_dsp_set_power_state(struct snd_sof_dev *sdev, - enum sof_d0_substate d0_substate) +static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value) { struct hdac_bus *bus = sof_to_bus(sdev); - u32 flags; int ret; - u8 value; /* Write to D0I3C after Command-In-Progress bit is cleared */ ret = hda_dsp_wait_d0i3c_done(sdev); @@ -354,7 +361,6 @@ int hda_dsp_set_power_state(struct snd_sof_dev *sdev, } /* Update D0I3C register */ - value = d0_substate == SOF_DSP_D0I3 ? SOF_HDA_VS_D0I3C_I3 : 0; snd_hdac_chip_updateb(bus, VS_D0I3C, SOF_HDA_VS_D0I3C_I3, value); /* Wait for cmd in progress to be cleared before exiting the function */ @@ -367,20 +373,218 @@ int hda_dsp_set_power_state(struct snd_sof_dev *sdev, dev_vdbg(bus->dev, "D0I3C updated, register = 0x%x\n", snd_hdac_chip_readb(bus, VS_D0I3C)); - if (d0_substate == SOF_DSP_D0I0) - flags = HDA_PM_PPG;/* prevent power gating in D0 */ - else - flags = HDA_PM_NO_DMA_TRACE;/* disable DMA trace in D0I3*/ + return 0; +} - /* sending pm_gate IPC */ - ret = hda_dsp_send_pm_gate_ipc(sdev, flags); +static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + u32 flags = 0; + int ret; + u8 value = 0; + + /* + * Sanity check for illegal state transitions + * The only allowed transitions are: + * 1. D3 -> D0I0 + * 2. D0I0 -> D0I3 + * 3. D0I3 -> D0I0 + */ + switch (sdev->dsp_power_state.state) { + case SOF_DSP_PM_D0: + /* Follow the sequence below for D0 substate transitions */ + break; + case SOF_DSP_PM_D3: + /* Follow regular flow for D3 -> D0 transition */ + return 0; + default: + dev_err(sdev->dev, "error: transition from %d to %d not allowed\n", + sdev->dsp_power_state.state, target_state->state); + return -EINVAL; + } + + /* Set flags and register value for D0 target substate */ + if (target_state->substate == SOF_HDA_DSP_PM_D0I3) { + value = SOF_HDA_VS_D0I3C_I3; + + /* + * Trace DMA is disabled by default when the DSP enters D0I3. + * But it can be kept enabled when the DSP enters D0I3 while the + * system is in S0 for debug. + */ + if (hda_enable_trace_D0I3_S0 && + sdev->system_suspend_target != SOF_SUSPEND_NONE) + flags = HDA_PM_NO_DMA_TRACE; + } else { + /* prevent power gating in D0I0 */ + flags = HDA_PM_PPG; + } + + /* update D0I3C register */ + ret = hda_dsp_update_d0i3c_register(sdev, value); if (ret < 0) + return ret; + + /* + * Notify the DSP of the state change. + * If this IPC fails, revert the D0I3C register update in order + * to prevent partial state change. + */ + ret = hda_dsp_send_pm_gate_ipc(sdev, flags); + if (ret < 0) { dev_err(sdev->dev, "error: PM_GATE ipc error %d\n", ret); + goto revert; + } + + return ret; + +revert: + /* fallback to the previous register value */ + value = value ? 0 : SOF_HDA_VS_D0I3C_I3; + + /* + * This can fail but return the IPC error to signal that + * the state change failed. + */ + hda_dsp_update_d0i3c_register(sdev, value); return ret; } +/* helper to log DSP state */ +static void hda_dsp_state_log(struct snd_sof_dev *sdev) +{ + switch (sdev->dsp_power_state.state) { + case SOF_DSP_PM_D0: + switch (sdev->dsp_power_state.substate) { + case SOF_HDA_DSP_PM_D0I0: + dev_dbg(sdev->dev, "Current DSP power state: D0I0\n"); + break; + case SOF_HDA_DSP_PM_D0I3: + dev_dbg(sdev->dev, "Current DSP power state: D0I3\n"); + break; + default: + dev_dbg(sdev->dev, "Unknown DSP D0 substate: %d\n", + sdev->dsp_power_state.substate); + break; + } + break; + case SOF_DSP_PM_D1: + dev_dbg(sdev->dev, "Current DSP power state: D1\n"); + break; + case SOF_DSP_PM_D2: + dev_dbg(sdev->dev, "Current DSP power state: D2\n"); + break; + case SOF_DSP_PM_D3_HOT: + dev_dbg(sdev->dev, "Current DSP power state: D3_HOT\n"); + break; + case SOF_DSP_PM_D3: + dev_dbg(sdev->dev, "Current DSP power state: D3\n"); + break; + case SOF_DSP_PM_D3_COLD: + dev_dbg(sdev->dev, "Current DSP power state: D3_COLD\n"); + break; + default: + dev_dbg(sdev->dev, "Unknown DSP power state: %d\n", + sdev->dsp_power_state.state); + break; + } +} + +/* + * All DSP power state transitions are initiated by the driver. + * If the requested state change fails, the error is simply returned. + * Further state transitions are attempted only when the set_power_save() op + * is called again either because of a new IPC sent to the DSP or + * during system suspend/resume. + */ +int hda_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + int ret = 0; + + /* + * When the DSP is already in D0I3 and the target state is D0I3, + * it could be the case that the DSP is in D0I3 during S0 + * and the system is suspending to S0Ix. Therefore, + * hda_dsp_set_D0_state() must be called to disable trace DMA + * by sending the PM_GATE IPC to the FW. + */ + if (target_state->substate == SOF_HDA_DSP_PM_D0I3 && + sdev->system_suspend_target == SOF_SUSPEND_S0IX) + goto set_state; + + /* + * For all other cases, return without doing anything if + * the DSP is already in the target state. + */ + if (target_state->state == sdev->dsp_power_state.state && + target_state->substate == sdev->dsp_power_state.substate) + return 0; + +set_state: + switch (target_state->state) { + case SOF_DSP_PM_D0: + ret = hda_dsp_set_D0_state(sdev, target_state); + break; + case SOF_DSP_PM_D3: + /* The only allowed transition is: D0I0 -> D3 */ + if (sdev->dsp_power_state.state == SOF_DSP_PM_D0 && + sdev->dsp_power_state.substate == SOF_HDA_DSP_PM_D0I0) + break; + + dev_err(sdev->dev, + "error: transition from %d to %d not allowed\n", + sdev->dsp_power_state.state, target_state->state); + return -EINVAL; + default: + dev_err(sdev->dev, "error: target state unsupported %d\n", + target_state->state); + return -EINVAL; + } + if (ret < 0) { + dev_err(sdev->dev, + "failed to set requested target DSP state %d substate %d\n", + target_state->state, target_state->substate); + return ret; + } + + sdev->dsp_power_state = *target_state; + hda_dsp_state_log(sdev); + return ret; +} + +/* + * Audio DSP states may transform as below:- + * + * Opportunistic D0I3 in S0 + * Runtime +---------------------+ Delayed D0i3 work timeout + * suspend | +--------------------+ + * +------------+ D0I0(active) | | + * | | <---------------+ | + * | +--------> | New IPC | | + * | |Runtime +--^--+---------^--+--+ (via mailbox) | | + * | |resume | | | | | | + * | | | | | | | | + * | | System| | | | | | + * | | resume| | S3/S0IX | | | | + * | | | | suspend | | S0IX | | + * | | | | | |suspend | | + * | | | | | | | | + * | | | | | | | | + * +-v---+-----------+--v-------+ | | +------+----v----+ + * | | | +-----------> | + * | D3 (suspended) | | | D0I3 | + * | | +--------------+ | + * | | System resume | | + * +----------------------------+ +----------------+ + * + * S0IX suspend: The DSP is in D0I3 if any D0I3-compatible streams + * ignored the suspend trigger. Otherwise the DSP + * is in D3. + */ + static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -390,6 +594,8 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #endif int ret; + hda_sdw_int_enable(sdev, false); + /* disable IPC interrupts */ hda_dsp_ipc_int_disable(sdev); @@ -486,10 +692,24 @@ int hda_dsp_resume(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct pci_dev *pci = to_pci_dev(sdev->dev); + const struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D0, + .substate = SOF_HDA_DSP_PM_D0I0, + }; + int ret; - if (sdev->s0_suspend) { + /* resume from D0I3 */ + if (sdev->dsp_power_state.state == SOF_DSP_PM_D0) { hda_codec_i915_display_power(sdev, true); + /* Set DSP power state */ + ret = snd_sof_dsp_set_power_state(sdev, &target_state); + if (ret < 0) { + dev_err(sdev->dev, "error: setting dsp state %d substate %d\n", + target_state.state, target_state.substate); + return ret; + } + /* restore L1SEN bit */ if (hda->l1_support_changed) snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -503,13 +723,26 @@ int hda_dsp_resume(struct snd_sof_dev *sdev) } /* init hda controller. DSP cores will be powered up during fw boot */ - return hda_resume(sdev, false); + ret = hda_resume(sdev, false); + if (ret < 0) + return ret; + + return snd_sof_dsp_set_power_state(sdev, &target_state); } int hda_dsp_runtime_resume(struct snd_sof_dev *sdev) { + const struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D0, + }; + int ret; + /* init hda controller. DSP cores will be powered up during fw boot */ - return hda_resume(sdev, true); + ret = hda_resume(sdev, true); + if (ret < 0) + return ret; + + return snd_sof_dsp_set_power_state(sdev, &target_state); } int hda_dsp_runtime_idle(struct snd_sof_dev *sdev) @@ -527,21 +760,47 @@ int hda_dsp_runtime_idle(struct snd_sof_dev *sdev) int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev) { + const struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D3, + }; + int ret; + /* stop hda controller and power dsp off */ - return hda_suspend(sdev, true); + ret = hda_suspend(sdev, true); + if (ret < 0) + return ret; + + return snd_sof_dsp_set_power_state(sdev, &target_state); } -int hda_dsp_suspend(struct snd_sof_dev *sdev) +int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct hdac_bus *bus = sof_to_bus(sdev); struct pci_dev *pci = to_pci_dev(sdev->dev); + const struct sof_dsp_power_state target_dsp_state = { + .state = target_state, + .substate = target_state == SOF_DSP_PM_D0 ? + SOF_HDA_DSP_PM_D0I3 : 0, + }; int ret; - if (sdev->s0_suspend) { + /* cancel any attempt for DSP D0I3 */ + cancel_delayed_work_sync(&hda->d0i3_work); + + if (target_state == SOF_DSP_PM_D0) { /* we can't keep a wakeref to display driver at suspend */ hda_codec_i915_display_power(sdev, false); + /* Set DSP power state */ + ret = snd_sof_dsp_set_power_state(sdev, &target_dsp_state); + if (ret < 0) { + dev_err(sdev->dev, "error: setting dsp state %d substate %d\n", + target_dsp_state.state, + target_dsp_state.substate); + return ret; + } + /* enable L1SEN to make sure the system can enter S0Ix */ hda->l1_support_changed = snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -562,7 +821,7 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev) return ret; } - return 0; + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) @@ -588,7 +847,7 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) */ if (stream->link_substream) { rtd = snd_pcm_substream_chip(stream->link_substream); - name = rtd->codec_dai->component->name; + name = asoc_rtd_to_codec(rtd, 0)->component->name; link = snd_hdac_ext_bus_get_link(bus, name); if (!link) return -EINVAL; @@ -606,3 +865,33 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) #endif return 0; } + +void hda_dsp_d0i3_work(struct work_struct *work) +{ + struct sof_intel_hda_dev *hdev = container_of(work, + struct sof_intel_hda_dev, + d0i3_work.work); + struct hdac_bus *bus = &hdev->hbus.core; + struct snd_sof_dev *sdev = dev_get_drvdata(bus->dev); + struct sof_dsp_power_state target_state; + int ret; + + target_state.state = SOF_DSP_PM_D0; + + /* DSP can enter D0I3 iff only D0I3-compatible streams are active */ + if (snd_sof_dsp_only_d0i3_compatible_stream_active(sdev)) + target_state.substate = SOF_HDA_DSP_PM_D0I3; + else + target_state.substate = SOF_HDA_DSP_PM_D0I0; + + /* remain in D0I0 */ + if (target_state.substate == SOF_HDA_DSP_PM_D0I0) + return; + + /* This can fail but error cannot be propagated */ + ret = snd_sof_dsp_set_power_state(sdev, &target_state); + if (ret < 0) + dev_err_ratelimited(sdev->dev, + "error: failed to set DSP state %d substate %d\n", + target_state.state, target_state.substate); +} diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 1837f66e361f..6062bb6011fb 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -106,7 +106,9 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) ret = reply.error; } else { /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size) { + if (reply.hdr.size != msg->reply_size && + /* getter payload is never known upfront */ + !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) { dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", msg->reply_size, reply.hdr.size); ret = -EINVAL; @@ -123,12 +125,6 @@ out: } -static bool hda_dsp_ipc_is_sof(uint32_t msg) -{ - return (msg & (HDA_DSP_IPC_PURGE_FW | 0xf << 9)) != msg || - (msg & HDA_DSP_IPC_PURGE_FW) != HDA_DSP_IPC_PURGE_FW; -} - /* IPC handler thread */ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) { @@ -174,17 +170,9 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) */ spin_lock_irq(&sdev->ipc_lock); - /* handle immediate reply from DSP core - ignore ROM messages */ - if (hda_dsp_ipc_is_sof(msg)) { - hda_dsp_ipc_get_reply(sdev); - snd_sof_ipc_reply(sdev, msg); - } - - /* wake up sleeper if we are loading code */ - if (sdev->code_loading) { - sdev->code_loading = 0; - wake_up(&sdev->waitq); - } + /* handle immediate reply from DSP core */ + hda_dsp_ipc_get_reply(sdev); + snd_sof_ipc_reply(sdev, msg); /* set the done bit */ hda_dsp_ipc_dsp_done(sdev); diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 8852184a2569..e1550ccd0a49 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -131,6 +131,12 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, goto err; } + /* set DONE bit to clear the reply IPC message */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + chip->ipc_ack, + chip->ipc_ack_mask, + chip->ipc_ack_mask); + /* step 5: power down corex */ ret = hda_dsp_core_power_down(sdev, chip->cores_mask & ~(HDA_DSP_CORE_MASK(0))); @@ -173,9 +179,6 @@ static int cl_trigger(struct snd_sof_dev *sdev, /* code loader is special case that reuses stream ops */ switch (cmd) { case SNDRV_PCM_TRIGGER_START: - wait_event_timeout(sdev->waitq, !sdev->code_loading, - HDA_DSP_CL_TRIGGER_TIMEOUT); - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, 1 << hstream->index, 1 << hstream->index); @@ -344,6 +347,24 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) } /* + * When a SoundWire link is in clock stop state, a Slave + * device may trigger in-band wakes for events such as jack + * insertion or acoustic event detection. This event will lead + * to a WAKEEN interrupt, handled by the PCI device and routed + * to PME if the PCI device is in D3. The resume function in + * audio PCI driver will be invoked by ACPI for PME event and + * initialize the device and process WAKEEN interrupt. + * + * The WAKEEN interrupt should be processed ASAP to prevent an + * interrupt flood, otherwise other interrupts, such IPC, + * cannot work normally. The WAKEEN is handled after the ROM + * is initialized successfully, which ensures power rails are + * enabled before accessing the SoundWire SHIM registers + */ + if (!sdev->first_boot) + hda_sdw_process_wakeen(sdev); + + /* * at this point DSP ROM has been initialized and * should be ready for code loading and firmware boot */ @@ -396,6 +417,19 @@ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) /* post fw run operations */ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) { + 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); + /* re-enable clock gating and power gating */ return hda_dsp_ctrl_clock_power_gating(sdev, true); } diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 23872f6e708d..a46a6baa1c3f 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -27,7 +27,7 @@ #define SDnFMT_BITS(x) ((x) << 4) #define SDnFMT_CHAN(x) ((x) << 0) -static inline u32 get_mult_div(struct snd_sof_dev *sdev, int rate) +u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate) { switch (rate) { case 8000: @@ -61,7 +61,7 @@ static inline u32 get_mult_div(struct snd_sof_dev *sdev, int rate) } }; -static inline u32 get_bits(struct snd_sof_dev *sdev, int sample_bits) +u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits) { switch (sample_bits) { case 8: @@ -95,8 +95,8 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, u32 size, rate, bits; size = params_buffer_bytes(params); - rate = get_mult_div(sdev, params_rate(params)); - bits = get_bits(sdev, params_width(params)); + rate = hda_dsp_get_mult_div(sdev, params_rate(params)); + bits = hda_dsp_get_bits(sdev, params_width(params)); hstream->substream = substream; diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index c0ab9bb2a797..5d386956906f 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -547,6 +547,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); + stream->substream = NULL; + return 0; } @@ -571,6 +573,22 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) return ret; } +static void +hda_dsp_set_bytes_transferred(struct hdac_stream *hstream, u64 buffer_size) +{ + u64 prev_pos, pos, num_bytes; + + div64_u64_rem(hstream->curr_pos, buffer_size, &prev_pos); + pos = snd_hdac_stream_get_pos_posbuf(hstream); + + if (pos < prev_pos) + num_bytes = (buffer_size - prev_pos) + pos; + else + num_bytes = pos - prev_pos; + + hstream->curr_pos += num_bytes; +} + static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) { struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); @@ -588,14 +606,19 @@ static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) snd_hdac_stream_writeb(s, SD_STS, sd_status); active = true; - if (!s->substream || + if ((!s->substream && !s->cstream) || !s->running || (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) continue; /* Inform ALSA only in case not do that with IPC */ - if (sof_hda->no_ipc_position) + if (s->substream && sof_hda->no_ipc_position) { snd_sof_pcm_period_elapsed(s->substream); + } else if (s->cstream) { + hda_dsp_set_bytes_transferred(s, + s->cstream->runtime->buffer_size); + snd_compr_fragment_elapsed(s->cstream); + } } } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 25946a1c2822..211e91e79eae 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -18,10 +18,14 @@ #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> +#include <linux/acpi.h> #include <linux/module.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_intel.h> #include <sound/intel-nhlt.h> #include <sound/sof.h> #include <sound/sof/xtensa.h> +#include "../sof-audio.h" #include "../ops.h" #include "hda.h" @@ -34,6 +38,235 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) + +/* + * The default for SoundWire clock stop quirks is to power gate the IP + * and do a Bus Reset, this will need to be modified when the DSP + * needs to remain in D0i3 so that the Master does not lose context + * and enumeration is not required on clock restart + */ +static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET; +module_param(sdw_clock_stop_quirks, int, 0444); +MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks"); + +static int sdw_params_stream(struct device *dev, + struct sdw_intel_stream_params_data *params_data) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_soc_dai *d = params_data->dai; + struct sof_ipc_dai_config config; + struct sof_ipc_reply reply; + int link_id = params_data->link_id; + int alh_stream_id = params_data->alh_stream_id; + int ret; + u32 size = sizeof(config); + + memset(&config, 0, size); + config.hdr.size = size; + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.type = SOF_DAI_INTEL_ALH; + config.dai_index = (link_id << 8) | (d->id); + config.alh.stream_id = alh_stream_id; + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config.hdr.cmd, &config, size, &reply, + sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n", + link_id, d->id, alh_stream_id); + } + + return ret; +} + +static int sdw_free_stream(struct device *dev, + struct sdw_intel_stream_free_data *free_data) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_soc_dai *d = free_data->dai; + struct sof_ipc_dai_config config; + struct sof_ipc_reply reply; + int link_id = free_data->link_id; + int ret; + u32 size = sizeof(config); + + memset(&config, 0, size); + config.hdr.size = size; + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.type = SOF_DAI_INTEL_ALH; + config.dai_index = (link_id << 8) | d->id; + config.alh.stream_id = 0xFFFF; /* invalid value on purpose */ + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config.hdr.cmd, &config, size, &reply, + sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to free stream for link %d dai->id %d\n", + link_id, d->id); + } + + return ret; +} + +static const struct sdw_intel_ops sdw_callback = { + .params_stream = sdw_params_stream, + .free_stream = sdw_free_stream, +}; + +void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) +{ + sdw_intel_enable_irq(sdev->bar[HDA_DSP_BAR], enable); +} + +static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + acpi_handle handle; + int ret; + + handle = ACPI_HANDLE(sdev->dev); + + /* save ACPI info for the probe step */ + hdev = sdev->pdata->hw_pdata; + + ret = sdw_intel_acpi_scan(handle, &hdev->info); + if (ret < 0) { + dev_err(sdev->dev, "%s failed\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int hda_sdw_probe(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + struct sdw_intel_res res; + void *sdw; + + hdev = sdev->pdata->hw_pdata; + + memset(&res, 0, sizeof(res)); + + res.mmio_base = sdev->bar[HDA_DSP_BAR]; + res.irq = sdev->ipc_irq; + res.handle = hdev->info.handle; + res.parent = sdev->dev; + res.ops = &sdw_callback; + res.dev = sdev->dev; + res.clock_stop_quirks = sdw_clock_stop_quirks; + + /* + * ops and arg fields are not populated for now, + * they will be needed when the DAI callbacks are + * provided + */ + + /* we could filter links here if needed, e.g for quirks */ + res.count = hdev->info.count; + res.link_mask = hdev->info.link_mask; + + sdw = sdw_intel_probe(&res); + if (!sdw) { + dev_err(sdev->dev, "error: SoundWire probe failed\n"); + return -EINVAL; + } + + /* save context */ + hdev->sdw = sdw; + + return 0; +} + +int hda_sdw_startup(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + + if (!hdev->sdw) + return 0; + + return sdw_intel_startup(hdev->sdw); +} + +static int hda_sdw_exit(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + + hda_sdw_int_enable(sdev, false); + + if (hdev->sdw) + sdw_intel_exit(hdev->sdw); + hdev->sdw = NULL; + + return 0; +} + +static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + bool ret = false; + u32 irq_status; + + hdev = sdev->pdata->hw_pdata; + + if (!hdev->sdw) + return ret; + + /* store status */ + irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS2); + + /* invalid message ? */ + if (irq_status == 0xffffffff) + goto out; + + /* SDW message ? */ + if (irq_status & HDA_DSP_REG_ADSPIS2_SNDW) + ret = true; + +out: + return ret; +} + +static irqreturn_t hda_dsp_sdw_thread(int irq, void *context) +{ + return sdw_intel_thread(irq, context); +} + +static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + if (hdev->sdw && + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_SNDW_WAKE_STS)) + return true; + + return false; +} + +void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + if (!hdev->sdw) + return; + + sdw_intel_process_wakeen_event(hdev->sdw); +} + +#endif + /* * Debug */ @@ -54,8 +287,7 @@ static int hda_dmic_num = -1; module_param_named(dmic_num, hda_dmic_num, int, 0444); MODULE_PARM_DESC(dmic_num, "SOF HDA DMIC number"); -static bool hda_codec_use_common_hdmi = - IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_COMMON_HDMI_CODEC); +static bool hda_codec_use_common_hdmi = IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI); module_param_named(use_common_hdmi, hda_codec_use_common_hdmi, bool, 0444); MODULE_PARM_DESC(use_common_hdmi, "SOF HDA use common HDMI codec driver"); #endif @@ -288,10 +520,8 @@ static int hda_init(struct snd_sof_dev *sdev) /* init i915 and HDMI codecs */ ret = hda_codec_i915_init(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: init i915 and HDMI codec failed\n"); - return ret; - } + if (ret < 0) + dev_warn(sdev->dev, "init of i915 and HDMI codec failed\n"); /* get controller capabilities */ ret = hda_dsp_ctrl_get_caps(sdev); @@ -349,9 +579,12 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev, static int hda_init_caps(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); + struct snd_sof_pdata *pdata = sdev->pdata; #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_ext_link *hlink; #endif + struct sof_intel_hda_dev *hdev = pdata->hw_pdata; + u32 link_mask; int ret = 0; device_disable_async_suspend(bus->dev); @@ -365,12 +598,37 @@ static int hda_init_caps(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(bus->dev, "error: init chip failed with ret: %d\n", ret); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - hda_codec_i915_exit(sdev); -#endif return ret; } + /* scan SoundWire capabilities exposed by DSDT */ + ret = hda_sdw_acpi_scan(sdev); + if (ret < 0) { + dev_dbg(sdev->dev, "skipping SoundWire, ACPI scan error\n"); + goto skip_soundwire; + } + + link_mask = hdev->info.link_mask; + if (!link_mask) { + dev_dbg(sdev->dev, "skipping SoundWire, no links enabled\n"); + goto skip_soundwire; + } + + /* + * probe/allocate SoundWire resources. + * The hardware configuration takes place in hda_sdw_startup + * after power rails are enabled. + * It's entirely possible to have a mix of I2S/DMIC/SoundWire + * devices, so we allocate the resources in all cases. + */ + ret = hda_sdw_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire probe error\n"); + return ret; + } + +skip_soundwire: + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); @@ -379,7 +637,7 @@ static int hda_init_caps(struct snd_sof_dev *sdev) hda_codec_probe_bus(sdev, hda_codec_use_common_hdmi); if (!HDA_IDISP_CODEC(bus->codec_mask)) - hda_codec_i915_exit(sdev); + hda_codec_i915_display_power(sdev, false); /* * we are done probing so decrement link counts @@ -427,6 +685,7 @@ static irqreturn_t hda_dsp_interrupt_handler(int irq, void *context) static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; /* deal with streams and controller first */ if (hda_dsp_check_stream_irq(sdev)) @@ -435,6 +694,12 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) if (hda_dsp_check_ipc_irq(sdev)) sof_ops(sdev)->irq_thread(irq, sdev); + if (hda_dsp_check_sdw_irq(sdev)) + hda_dsp_sdw_thread(irq, hdev->sdw); + + if (hda_sdw_check_wakeen_irq(sdev)) + hda_sdw_process_wakeen(sdev); + /* enable GIE interrupt */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, @@ -590,12 +855,11 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) hda_dsp_ctrl_ppcap_enable(sdev, true); hda_dsp_ctrl_ppcap_int_enable(sdev, true); - /* initialize waitq for code loading */ - init_waitqueue_head(&sdev->waitq); - /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET; + INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work); + return 0; free_ipc_irq: @@ -621,11 +885,16 @@ int hda_dsp_remove(struct snd_sof_dev *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); + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* codec removal, invoke bus_device_remove */ snd_hdac_ext_bus_device_remove(bus); #endif + hda_sdw_exit(sdev); + if (!IS_ERR_OR_NULL(hda->dmic_dev)) platform_device_unregister(hda->dmic_dev); @@ -694,12 +963,11 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) /* * If no machine driver is found, then: * - * hda machine driver is used if : - * 1. there is one HDMI codec and one external HDAudio codec - * 2. only HDMI codec + * generic hda machine driver can handle: + * - one HDMI codec, and/or + * - one external HDAudio codec */ - if (!pdata->machine && codec_num <= 2 && - HDA_IDISP_CODEC(bus->codec_mask)) { + if (!pdata->machine && codec_num <= 2) { hda_mach = snd_soc_acpi_intel_hda_machines; /* topology: use the info from hda_machines */ @@ -709,7 +977,7 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) dev_info(bus->dev, "using HDA machine driver %s now\n", hda_mach->drv_name); - if (codec_num == 1) + if (codec_num == 1 && HDA_IDISP_CODEC(bus->codec_mask)) idisp_str = "-idisp"; else idisp_str = ""; @@ -763,6 +1031,123 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) } #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) +/* Check if all Slaves defined on the link can be found */ +static bool link_slaves_found(struct snd_sof_dev *sdev, + const struct snd_soc_acpi_link_adr *link, + struct sdw_intel_ctx *sdw) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct sdw_intel_slave_id *ids = sdw->ids; + int num_slaves = sdw->num_slaves; + unsigned int part_id, link_id, unique_id, mfg_id; + int i, j; + + for (i = 0; i < link->num_adr; i++) { + u64 adr = link->adr_d[i].adr; + + mfg_id = SDW_MFG_ID(adr); + part_id = SDW_PART_ID(adr); + link_id = SDW_DISCO_LINK_ID(adr); + for (j = 0; j < num_slaves; j++) { + if (ids[j].link_id != link_id || + ids[j].id.part_id != part_id || + ids[j].id.mfg_id != mfg_id) + continue; + /* + * we have to check unique id + * if there is more than one + * Slave on the link + */ + unique_id = SDW_UNIQUE_ID(adr); + if (link->num_adr == 1 || + ids[j].id.unique_id == SDW_IGNORED_UNIQUE_ID || + ids[j].id.unique_id == unique_id) { + dev_dbg(bus->dev, + "found %x at link %d\n", + part_id, link_id); + break; + } + } + if (j == num_slaves) { + dev_dbg(bus->dev, + "Slave %x not found\n", + part_id); + return false; + } + } + return true; +} + +static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct snd_soc_acpi_link_adr *link; + struct hdac_bus *bus = sof_to_bus(sdev); + struct snd_soc_acpi_mach *mach; + struct sof_intel_hda_dev *hdev; + u32 link_mask; + int i; + + hdev = pdata->hw_pdata; + link_mask = hdev->info.link_mask; + + /* + * Select SoundWire machine driver if needed using the + * alternate tables. This case deals with SoundWire-only + * machines, for mixed cases with I2C/I2S the detection relies + * on the HID list. + */ + if (link_mask && !pdata->machine) { + for (mach = pdata->desc->alt_machines; + mach && mach->link_mask; mach++) { + if (mach->link_mask != link_mask) + continue; + + /* No need to match adr if there is no links defined */ + if (!mach->links) + break; + + link = mach->links; + for (i = 0; i < hdev->info.count && link->num_adr; + i++, link++) { + /* + * Try next machine if any expected Slaves + * are not found on this link. + */ + if (!link_slaves_found(sdev, link, hdev->sdw)) + break; + } + /* Found if all Slaves are checked */ + if (i == hdev->info.count || !link->num_adr) + break; + } + if (mach && mach->link_mask) { + dev_dbg(bus->dev, + "SoundWire machine driver %s topology %s\n", + mach->drv_name, + mach->sof_tplg_filename); + 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); + pdata->fw_filename = mach->sof_fw_filename; + pdata->tplg_filename = mach->sof_tplg_filename; + } else { + dev_info(sdev->dev, + "No SoundWire machine driver found\n"); + } + } + + return 0; +} +#else +static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +{ + return 0; +} +#endif + void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, struct device *dev) { @@ -782,9 +1167,19 @@ void hda_machine_select(struct snd_sof_dev *sdev) if (mach) { 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; + } } /* + * If I2S fails, try SoundWire + */ + hda_sdw_machine_select(sdev); + + /* * Choose HDA generic machine driver if mach is NULL. * Otherwise, set certain mach params. */ diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 6191d9192fae..e9825798de77 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -11,6 +11,9 @@ #ifndef __SOF_INTEL_HDA_H #define __SOF_INTEL_HDA_H +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_intel.h> +#include <sound/compress_driver.h> #include <sound/hda_codec.h> #include <sound/hdaudio_ext.h> #include "shim.h" @@ -174,7 +177,6 @@ * value cannot be read back within the specified time. */ #define HDA_DSP_STREAM_RUN_TIMEOUT 300 -#define HDA_DSP_CL_TRIGGER_TIMEOUT 300 #define HDA_DSP_SPIB_ENABLE 1 #define HDA_DSP_SPIB_DISABLE 0 @@ -230,6 +232,9 @@ #define HDA_DSP_REG_ADSPIC2 (HDA_DSP_GEN_BASE + 0x10) #define HDA_DSP_REG_ADSPIS2 (HDA_DSP_GEN_BASE + 0x14) +#define HDA_DSP_REG_ADSPIS2_SNDW BIT(5) +#define HDA_DSP_REG_SNDW_WAKE_STS 0x2C192 + /* Intel HD Audio Inter-Processor Communication Registers */ #define HDA_DSP_IPC_BASE 0x40 #define HDA_DSP_REG_HIPCT (HDA_DSP_IPC_BASE + 0x00) @@ -348,7 +353,13 @@ /* Number of DAIs */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) +#define SOF_SKL_NUM_DAIS 16 +#else #define SOF_SKL_NUM_DAIS 15 +#endif + #else #define SOF_SKL_NUM_DAIS 8 #endif @@ -392,6 +403,19 @@ struct sof_intel_dsp_bdl { #define SOF_HDA_PLAYBACK 0 #define SOF_HDA_CAPTURE 1 +/* + * Time in ms for opportunistic D0I3 entry delay. + * This has been deliberately chosen to be long to avoid race conditions. + * Could be optimized in future. + */ +#define SOF_HDA_D0I3_WORK_DELAY_MS 5000 + +/* HDA DSP D0 substate */ +enum sof_hda_D0_substate { + SOF_HDA_DSP_PM_D0I0, /* default D0 substate */ + SOF_HDA_DSP_PM_D0I3, /* low power D0 substate */ +}; + /* represents DSP HDA controller frontend - i.e. host facing control */ struct sof_intel_hda_dev { @@ -414,6 +438,15 @@ struct sof_intel_hda_dev { /* DMIC device */ struct platform_device *dmic_dev; + + /* delayed work to enter D0I3 opportunistically */ + struct delayed_work d0i3_work; + + /* ACPI information stored between scan and probe steps */ + struct sdw_intel_acpi_info info; + + /* sdw context allocated by SoundWire driver */ + struct sdw_intel_ctx *sdw; }; static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -469,9 +502,9 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev); void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev); int hda_dsp_set_power_state(struct snd_sof_dev *sdev, - enum sof_d0_substate d0_substate); + const struct sof_dsp_power_state *target_state); -int hda_dsp_suspend(struct snd_sof_dev *sdev); +int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state); int hda_dsp_resume(struct snd_sof_dev *sdev); int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev); int hda_dsp_runtime_resume(struct snd_sof_dev *sdev); @@ -481,10 +514,13 @@ void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags); void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); void hda_ipc_dump(struct snd_sof_dev *sdev); void hda_ipc_irq_dump(struct snd_sof_dev *sdev); +void hda_dsp_d0i3_work(struct work_struct *work); /* * DSP PCM Operations. */ +u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate); +u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits); int hda_dsp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); int hda_dsp_pcm_close(struct snd_sof_dev *sdev, @@ -533,6 +569,29 @@ int hda_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, const struct sof_ipc_pcm_params_reply *reply); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) +/* + * Probe Compress Operations. + */ +int hda_probe_compr_assign(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai); +int hda_probe_compr_free(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai); +int hda_probe_compr_set_params(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai); +int hda_probe_compr_trigger(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai); +int hda_probe_compr_pointer(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai); +#endif + /* * DSP IPC Operations. */ @@ -606,6 +665,61 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); int hda_dsp_trace_release(struct snd_sof_dev *sdev); int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); +/* + * SoundWire support + */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) + +int hda_sdw_startup(struct snd_sof_dev *sdev); +void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable); +void hda_sdw_process_wakeen(struct snd_sof_dev *sdev); + +#else + +static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_probe(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_startup(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_exit(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) +{ +} + +static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev) +{ + return false; +} + +static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context) +{ + return IRQ_HANDLED; +} + +static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +{ + return false; +} + +static inline void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) +{ +} +#endif + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 78aa1da7c7a9..1c6794918cbb 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -214,15 +214,17 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, snd_sof_handle_fw_exception(ipc->sdev); ret = -ETIMEDOUT; } else { - /* copy the data returned from DSP */ ret = msg->reply_error; - if (msg->reply_size) - memcpy(reply_data, msg->reply_data, msg->reply_size); - if (ret < 0) + if (ret < 0) { dev_err(sdev->dev, "error: ipc error for 0x%x size %zu\n", hdr->cmd, msg->reply_size); - else + } else { ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd); + if (msg->reply_size) + /* copy the data returned from DSP */ + memcpy(reply_data, msg->reply_data, + msg->reply_size); + } } return ret; @@ -268,7 +270,6 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, spin_unlock_irq(&sdev->ipc_lock); if (ret < 0) { - /* So far IPC TX never fails, consider making the above void */ dev_err_ratelimited(sdev->dev, "error: ipc tx failed with error %d\n", ret); @@ -289,6 +290,32 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes) { + const struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D0, + }; + int ret; + + /* ensure the DSP is in D0 before sending a new IPC */ + ret = snd_sof_dsp_set_power_state(ipc->sdev, &target_state); + if (ret < 0) { + dev_err(ipc->sdev->dev, "error: resuming DSP %d\n", ret); + return ret; + } + + return sof_ipc_tx_message_no_pm(ipc, header, msg_data, msg_bytes, + reply_data, reply_bytes); +} +EXPORT_SYMBOL(sof_ipc_tx_message); + +/* + * send IPC message from host to DSP without modifying the DSP state. + * This will be used for IPC's that can be handled by the DSP + * even in a low-power D0 substate. + */ +int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, + void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes) +{ int ret; if (msg_bytes > SOF_IPC_MSG_MAX_SIZE || @@ -305,7 +332,7 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, return ret; } -EXPORT_SYMBOL(sof_ipc_tx_message); +EXPORT_SYMBOL(sof_ipc_tx_message_no_pm); /* handle reply message from DSP */ int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index fc4ab51bacf4..1f2e0be812bd 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -95,9 +95,6 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) /* process structure data */ switch (ext_hdr->type) { - case SOF_IPC_EXT_DMA_BUFFER: - ret = 0; - break; case SOF_IPC_EXT_WINDOW: ret = get_ext_windows(sdev, ext_hdr); break; @@ -469,9 +466,6 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) const char *fw_filename; int ret; - /* set code loading condition to true */ - sdev->code_loading = 1; - /* Don't request firmware again if firmware is already requested */ if (plat_data->fw) return 0; diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index e929a6e0058f..a771500ac442 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -146,10 +146,11 @@ static inline int snd_sof_dsp_resume(struct snd_sof_dev *sdev) return 0; } -static inline int snd_sof_dsp_suspend(struct snd_sof_dev *sdev) +static inline int snd_sof_dsp_suspend(struct snd_sof_dev *sdev, + u32 target_state) { if (sof_ops(sdev)->suspend) - return sof_ops(sdev)->suspend(sdev); + return sof_ops(sdev)->suspend(sdev, target_state); return 0; } @@ -193,14 +194,15 @@ static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq) return 0; } -static inline int snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev, - enum sof_d0_substate substate) +static inline int +snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) { if (sof_ops(sdev)->set_power_state) - return sof_ops(sdev)->set_power_state(sdev, substate); + return sof_ops(sdev)->set_power_state(sdev, target_state); - /* D0 substate is not supported */ - return -ENOTSUPP; + /* D0 substate is not supported, do nothing here. */ + return 0; } /* debug */ @@ -391,6 +393,49 @@ snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) +static inline int +snd_sof_probe_compr_assign(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, struct snd_soc_dai *dai) +{ + return sof_ops(sdev)->probe_assign(sdev, cstream, dai); +} + +static inline int +snd_sof_probe_compr_free(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, struct snd_soc_dai *dai) +{ + return sof_ops(sdev)->probe_free(sdev, cstream, dai); +} + +static inline int +snd_sof_probe_compr_set_params(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, struct snd_soc_dai *dai) +{ + return sof_ops(sdev)->probe_set_params(sdev, cstream, params, dai); +} + +static inline int +snd_sof_probe_compr_trigger(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai) +{ + return sof_ops(sdev)->probe_trigger(sdev, cstream, cmd, dai); +} + +static inline int +snd_sof_probe_compr_pointer(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) +{ + if (sof_ops(sdev) && sof_ops(sdev)->probe_pointer) + return sof_ops(sdev)->probe_pointer(sdev, cstream, tstamp, dai); + + return 0; +} +#endif + /* machine driver */ static inline int snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 29435ba2d329..47cd741f2a8c 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -16,6 +16,9 @@ #include "sof-priv.h" #include "sof-audio.h" #include "ops.h" +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) +#include "compress.h" +#endif /* Create DMA buffer page table for DSP */ static int create_page_table(struct snd_soc_component *component, @@ -54,7 +57,7 @@ static int sof_pcm_dsp_params(struct snd_sof_pcm *spcm, struct snd_pcm_substream /* * sof pcm period elapse work */ -static void sof_pcm_period_elapsed_work(struct work_struct *work) +void snd_sof_pcm_period_elapsed_work(struct work_struct *work) { struct snd_sof_pcm_stream *sps = container_of(work, struct snd_sof_pcm_stream, @@ -372,7 +375,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; break; case SNDRV_PCM_TRIGGER_SUSPEND: - if (sdev->s0_suspend && + if (sdev->system_suspend_target == SOF_SUSPEND_S0IX && spcm->stream[substream->stream].d0i3_compatible) { /* * trap the event, not sending trigger stop to @@ -472,8 +475,6 @@ static int sof_pcm_open(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: open stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); - INIT_WORK(&spcm->stream[substream->stream].period_elapsed_work, - sof_pcm_period_elapsed_work); caps = &spcm->pcm.caps[substream->stream]; @@ -598,8 +599,7 @@ static int sof_pcm_new(struct snd_soc_component *component, snd_pcm_set_managed_buffer(pcm->streams[stream].substream, SNDRV_DMA_TYPE_DEV_SG, sdev->dev, - le32_to_cpu(caps->buffer_size_min), - le32_to_cpu(caps->buffer_size_max)); + 0, le32_to_cpu(caps->buffer_size_max)); capture: stream = SNDRV_PCM_STREAM_CAPTURE; @@ -621,8 +621,7 @@ capture: snd_pcm_set_managed_buffer(pcm->streams[stream].substream, SNDRV_DMA_TYPE_DEV_SG, sdev->dev, - le32_to_cpu(caps->buffer_size_min), - le32_to_cpu(caps->buffer_size_max)); + 0, le32_to_cpu(caps->buffer_size_max)); return 0; } @@ -788,6 +787,10 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) pd->compr_ops = &sof_compressed_ops; #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) + /* override cops when probe support is enabled */ + pd->compr_ops = &sof_probe_compressed_ops; +#endif pd->pcm_construct = sof_pcm_new; pd->ignore_machine = drv_name; pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index a0cde053b61a..c410822d9920 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -12,6 +12,42 @@ #include "sof-priv.h" #include "sof-audio.h" +/* + * Helper function to determine the target DSP state during + * system suspend. This function only cares about the device + * D-states. Platform-specific substates, if any, should be + * handled by the platform-specific parts. + */ +static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev) +{ + u32 target_dsp_state; + + switch (sdev->system_suspend_target) { + case SOF_SUSPEND_S3: + /* DSP should be in D3 if the system is suspending to S3 */ + target_dsp_state = SOF_DSP_PM_D3; + break; + case SOF_SUSPEND_S0IX: + /* + * Currently, the only criterion for retaining the DSP in D0 + * is that there are streams that ignored the suspend trigger. + * Additional criteria such Soundwire clock-stop mode and + * device suspend latency considerations will be added later. + */ + if (snd_sof_stream_suspend_ignored(sdev)) + target_dsp_state = SOF_DSP_PM_D0; + else + target_dsp_state = SOF_DSP_PM_D3; + break; + default: + /* This case would be during runtime suspend */ + target_dsp_state = SOF_DSP_PM_D3; + break; + } + + return target_dsp_state; +} + static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) { struct sof_ipc_pm_ctx pm_ctx; @@ -50,6 +86,7 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev) static int sof_resume(struct device *dev, bool runtime_resume) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + u32 old_state = sdev->dsp_power_state.state; int ret; /* do nothing if dsp resume callbacks are not set */ @@ -74,6 +111,10 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } + /* Nothing further to do if resuming from a low-power D0 substate */ + if (!runtime_resume && old_state == SOF_DSP_PM_D0) + return 0; + sdev->fw_state = SOF_FW_BOOT_PREPARE; /* load the firmware */ @@ -124,15 +165,13 @@ static int sof_resume(struct device *dev, bool runtime_resume) "error: ctx_restore ipc error during resume %d\n", ret); - /* initialize default D0 sub-state */ - sdev->d0_substate = SOF_DSP_D0I0; - return ret; } static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + u32 target_state = 0; int ret; /* do nothing if dsp suspend callback is not set */ @@ -140,10 +179,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) return 0; if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) - goto power_down; - - /* release trace */ - snd_sof_release_trace(sdev); + goto suspend; /* set restore_stream for all streams during system suspend */ if (!runtime_suspend) { @@ -156,6 +192,15 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) } } + target_state = snd_sof_dsp_power_target(sdev); + + /* Skip to platform-specific suspend if DSP is entering D0 */ + if (target_state == SOF_DSP_PM_D0) + goto suspend; + + /* release trace */ + snd_sof_release_trace(sdev); + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) /* cache debugfs contents during runtime suspend */ if (runtime_suspend) @@ -179,22 +224,26 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) ret); } -power_down: +suspend: /* return if the DSP was not probed successfully */ if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED) return 0; - /* power down all DSP cores */ + /* platform-specific suspend */ if (runtime_suspend) ret = snd_sof_dsp_runtime_suspend(sdev); else - ret = snd_sof_dsp_suspend(sdev); + ret = snd_sof_dsp_suspend(sdev, target_state); if (ret < 0) dev_err(sdev->dev, "error: failed to power down DSP during suspend %d\n", ret); + /* Do not reset FW state if DSP is in D0 */ + if (target_state == SOF_DSP_PM_D0) + return ret; + /* reset FW state */ sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; @@ -221,112 +270,14 @@ int snd_sof_runtime_resume(struct device *dev) } EXPORT_SYMBOL(snd_sof_runtime_resume); -int snd_sof_set_d0_substate(struct snd_sof_dev *sdev, - enum sof_d0_substate d0_substate) -{ - int ret; - - if (sdev->d0_substate == d0_substate) - return 0; - - /* do platform specific set_state */ - ret = snd_sof_dsp_set_power_state(sdev, d0_substate); - if (ret < 0) - return ret; - - /* update dsp D0 sub-state */ - sdev->d0_substate = d0_substate; - - return 0; -} -EXPORT_SYMBOL(snd_sof_set_d0_substate); - -/* - * Audio DSP states may transform as below:- - * - * D0I3 compatible stream - * Runtime +---------------------+ opened only, timeout - * suspend | +--------------------+ - * +------------+ D0(active) | | - * | | <---------------+ | - * | +--------> | | | - * | |Runtime +--^--+---------^--+--+ The last | | - * | |resume | | | | opened D0I3 | | - * | | | | | | compatible | | - * | | resume| | | | stream closed | | - * | | from | | D3 | | | | - * | | D3 | |suspend | | d0i3 | | - * | | | | | |suspend | | - * | | | | | | | | - * | | | | | | | | - * +-v---+-----------+--v-------+ | | +------+----v----+ - * | | | +-----------> | - * | D3 (suspended) | | | D0I3 +-----+ - * | | +--------------+ | | - * | | resume from | | | - * +-------------------^--------+ d0i3 suspend +----------------+ | - * | | - * | D3 suspend | - * +------------------------------------------------+ - * - * d0i3_suspend = s0_suspend && D0I3 stream opened, - * D3 suspend = !d0i3_suspend, - */ - int snd_sof_resume(struct device *dev) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - int ret; - - if (snd_sof_dsp_d0i3_on_suspend(sdev)) { - /* resume from D0I3 */ - dev_dbg(sdev->dev, "DSP will exit from D0i3...\n"); - ret = snd_sof_set_d0_substate(sdev, SOF_DSP_D0I0); - if (ret == -ENOTSUPP) { - /* fallback to resume from D3 */ - dev_dbg(sdev->dev, "D0i3 not supported, fall back to resume from D3...\n"); - goto d3_resume; - } else if (ret < 0) { - dev_err(sdev->dev, "error: failed to exit from D0I3 %d\n", - ret); - return ret; - } - - /* platform-specific resume from D0i3 */ - return snd_sof_dsp_resume(sdev); - } - -d3_resume: - /* resume from D3 */ return sof_resume(dev, false); } EXPORT_SYMBOL(snd_sof_resume); int snd_sof_suspend(struct device *dev) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - int ret; - - if (snd_sof_dsp_d0i3_on_suspend(sdev)) { - /* suspend to D0i3 */ - dev_dbg(sdev->dev, "DSP is trying to enter D0i3...\n"); - ret = snd_sof_set_d0_substate(sdev, SOF_DSP_D0I3); - if (ret == -ENOTSUPP) { - /* fallback to D3 suspend */ - dev_dbg(sdev->dev, "D0i3 not supported, fall back to D3...\n"); - goto d3_suspend; - } else if (ret < 0) { - dev_err(sdev->dev, "error: failed to enter D0I3, %d\n", - ret); - return ret; - } - - /* platform-specific suspend to D0i3 */ - return snd_sof_dsp_suspend(sdev); - } - -d3_suspend: - /* suspend to D3 */ return sof_suspend(dev, false); } EXPORT_SYMBOL(snd_sof_suspend); @@ -336,10 +287,13 @@ int snd_sof_prepare(struct device *dev) struct snd_sof_dev *sdev = dev_get_drvdata(dev); #if defined(CONFIG_ACPI) - sdev->s0_suspend = acpi_target_system_state() == ACPI_STATE_S0; + if (acpi_target_system_state() == ACPI_STATE_S0) + sdev->system_suspend_target = SOF_SUSPEND_S0IX; + else + sdev->system_suspend_target = SOF_SUSPEND_S3; #else /* will suspend to S3 by default */ - sdev->s0_suspend = false; + sdev->system_suspend_target = SOF_SUSPEND_S3; #endif return 0; @@ -350,6 +304,6 @@ void snd_sof_complete(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); - sdev->s0_suspend = false; + sdev->system_suspend_target = SOF_SUSPEND_NONE; } EXPORT_SYMBOL(snd_sof_complete); diff --git a/sound/soc/sof/probe.c b/sound/soc/sof/probe.c new file mode 100644 index 000000000000..c38169fe00c5 --- /dev/null +++ b/sound/soc/sof/probe.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: (GPL-2.0 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) 2019-2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski <cezary.rojewski@intel.com> +// + +#include "sof-priv.h" +#include "probe.h" + +/** + * sof_ipc_probe_init - initialize data probing + * @sdev: SOF sound device + * @stream_tag: Extractor stream tag + * @buffer_size: DMA buffer size to set for extractor + * + * Host chooses whether extraction is supported or not by providing + * valid stream tag to DSP. Once specified, stream described by that + * tag will be tied to DSP for extraction for the entire lifetime of + * probe. + * + * Probing is initialized only once and each INIT request must be + * matched by DEINIT call. + */ +int sof_ipc_probe_init(struct snd_sof_dev *sdev, + u32 stream_tag, size_t buffer_size) +{ + struct sof_ipc_probe_dma_add_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, dma, 1); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; + msg->num_elems = 1; + msg->dma[0].stream_tag = stream_tag; + msg->dma[0].dma_buffer_size = buffer_size; + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_init); + +/** + * sof_ipc_probe_deinit - cleanup after data probing + * @sdev: SOF sound device + * + * Host sends DEINIT request to free previously initialized probe + * on DSP side once it is no longer needed. DEINIT only when there + * are no probes connected and with all injectors detached. + */ +int sof_ipc_probe_deinit(struct snd_sof_dev *sdev) +{ + struct sof_ipc_cmd_hdr msg; + struct sof_ipc_reply reply; + + msg.size = sizeof(msg); + msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; + + return sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, + &reply, sizeof(reply)); +} +EXPORT_SYMBOL(sof_ipc_probe_deinit); + +static int sof_ipc_probe_info(struct snd_sof_dev *sdev, unsigned int cmd, + void **params, size_t *num_params) +{ + struct sof_ipc_probe_info_params msg = {{{0}}}; + struct sof_ipc_probe_info_params *reply; + size_t bytes; + int ret; + + *params = NULL; + *num_params = 0; + + reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!reply) + return -ENOMEM; + msg.rhdr.hdr.size = sizeof(msg); + msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; + + ret = sof_ipc_tx_message(sdev->ipc, msg.rhdr.hdr.cmd, &msg, + msg.rhdr.hdr.size, reply, SOF_IPC_MSG_MAX_SIZE); + if (ret < 0 || reply->rhdr.error < 0) + goto exit; + + if (!reply->num_elems) + goto exit; + + if (cmd == SOF_IPC_PROBE_DMA_INFO) + bytes = sizeof(reply->dma[0]); + else + bytes = sizeof(reply->desc[0]); + bytes *= reply->num_elems; + *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); + if (!*params) { + ret = -ENOMEM; + goto exit; + } + *num_params = reply->num_elems; + +exit: + kfree(reply); + return ret; +} + +/** + * sof_ipc_probe_dma_info - retrieve list of active injection dmas + * @sdev: SOF sound device + * @dma: Returned list of active dmas + * @num_dma: Returned count of active dmas + * + * Host sends DMA_INFO request to obtain list of injection dmas it + * can use to transfer data over with. + * + * Note that list contains only injection dmas as there is only one + * extractor (dma) and it is always assigned on probing init. + * DSP knows exactly where data from extraction probes is going to, + * which is not the case for injection where multiple streams + * could be engaged. + */ +int sof_ipc_probe_dma_info(struct snd_sof_dev *sdev, + struct sof_probe_dma **dma, size_t *num_dma) +{ + return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_DMA_INFO, + (void **)dma, num_dma); +} +EXPORT_SYMBOL(sof_ipc_probe_dma_info); + +/** + * sof_ipc_probe_dma_add - attach to specified dmas + * @sdev: SOF sound device + * @dma: List of streams (dmas) to attach to + * @num_dma: Number of elements in @dma + * + * Contrary to extraction, injection streams are never assigned + * on init. Before attempting any data injection, host is responsible + * for specifying streams which will be later used to transfer data + * to connected probe points. + */ +int sof_ipc_probe_dma_add(struct snd_sof_dev *sdev, + struct sof_probe_dma *dma, size_t num_dma) +{ + struct sof_ipc_probe_dma_add_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, dma, num_dma); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_dma; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_ADD; + memcpy(&msg->dma[0], dma, size - sizeof(*msg)); + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_dma_add); + +/** + * sof_ipc_probe_dma_remove - detach from specified dmas + * @sdev: SOF sound device + * @stream_tag: List of stream tags to detach from + * @num_stream_tag: Number of elements in @stream_tag + * + * Host sends DMA_REMOVE request to free previously attached stream + * from being occupied for injection. Each detach operation should + * match equivalent DMA_ADD. Detach only when all probes tied to + * given stream have been disconnected. + */ +int sof_ipc_probe_dma_remove(struct snd_sof_dev *sdev, + unsigned int *stream_tag, size_t num_stream_tag) +{ + struct sof_ipc_probe_dma_remove_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, stream_tag, num_stream_tag); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_stream_tag; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_REMOVE; + memcpy(&msg->stream_tag[0], stream_tag, size - sizeof(*msg)); + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_dma_remove); + +/** + * sof_ipc_probe_points_info - retrieve list of active probe points + * @sdev: SOF sound device + * @desc: Returned list of active probes + * @num_desc: Returned count of active probes + * + * Host sends PROBE_POINT_INFO request to obtain list of active probe + * points, valid for disconnection when given probe is no longer + * required. + */ +int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, + struct sof_probe_point_desc **desc, size_t *num_desc) +{ + return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_POINT_INFO, + (void **)desc, num_desc); +} +EXPORT_SYMBOL(sof_ipc_probe_points_info); + +/** + * sof_ipc_probe_points_add - connect specified probes + * @sdev: SOF sound device + * @desc: List of probe points to connect + * @num_desc: Number of elements in @desc + * + * Dynamically connects to provided set of endpoints. Immediately + * after connection is established, host must be prepared to + * transfer data from or to target stream given the probing purpose. + * + * Each probe point should be removed using PROBE_POINT_REMOVE + * request when no longer needed. + */ +int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, + struct sof_probe_point_desc *desc, size_t num_desc) +{ + struct sof_ipc_probe_point_add_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, desc, num_desc); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_desc; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; + memcpy(&msg->desc[0], desc, size - sizeof(*msg)); + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_points_add); + +/** + * sof_ipc_probe_points_remove - disconnect specified probes + * @sdev: SOF sound device + * @buffer_id: List of probe points to disconnect + * @num_buffer_id: Number of elements in @desc + * + * Removes previously connected probes from list of active probe + * points and frees all resources on DSP side. + */ +int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, + unsigned int *buffer_id, size_t num_buffer_id) +{ + struct sof_ipc_probe_point_remove_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, buffer_id, num_buffer_id); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_buffer_id; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; + memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_points_remove); diff --git a/sound/soc/sof/probe.h b/sound/soc/sof/probe.h new file mode 100644 index 000000000000..45daa5552834 --- /dev/null +++ b/sound/soc/sof/probe.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: (GPL-2.0 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) 2019-2020 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski <cezary.rojewski@intel.com> + */ + +#ifndef __SOF_PROBE_H +#define __SOF_PROBE_H + +#include <sound/sof/header.h> + +struct snd_sof_dev; + +#define SOF_PROBE_INVALID_NODE_ID UINT_MAX + +struct sof_probe_dma { + unsigned int stream_tag; + unsigned int dma_buffer_size; +} __packed; + +enum sof_connection_purpose { + SOF_CONNECTION_PURPOSE_EXTRACT = 1, + SOF_CONNECTION_PURPOSE_INJECT, +}; + +struct sof_probe_point_desc { + unsigned int buffer_id; + unsigned int purpose; + unsigned int stream_tag; +} __packed; + +struct sof_ipc_probe_dma_add_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + struct sof_probe_dma dma[0]; +} __packed; + +struct sof_ipc_probe_info_params { + struct sof_ipc_reply rhdr; + unsigned int num_elems; + union { + struct sof_probe_dma dma[0]; + struct sof_probe_point_desc desc[0]; + }; +} __packed; + +struct sof_ipc_probe_dma_remove_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + unsigned int stream_tag[0]; +} __packed; + +struct sof_ipc_probe_point_add_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + struct sof_probe_point_desc desc[0]; +} __packed; + +struct sof_ipc_probe_point_remove_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + unsigned int buffer_id[0]; +} __packed; + +int sof_ipc_probe_init(struct snd_sof_dev *sdev, + u32 stream_tag, size_t buffer_size); +int sof_ipc_probe_deinit(struct snd_sof_dev *sdev); +int sof_ipc_probe_dma_info(struct snd_sof_dev *sdev, + struct sof_probe_dma **dma, size_t *num_dma); +int sof_ipc_probe_dma_add(struct snd_sof_dev *sdev, + struct sof_probe_dma *dma, size_t num_dma); +int sof_ipc_probe_dma_remove(struct snd_sof_dev *sdev, + unsigned int *stream_tag, size_t num_stream_tag); +int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, + struct sof_probe_point_desc **desc, size_t *num_desc); +int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, + struct sof_probe_point_desc *desc, size_t num_desc); +int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, + unsigned int *buffer_id, size_t num_buffer_id); + +#endif diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 0d8f65b9ae25..fc4ed2a8a914 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -11,7 +11,40 @@ #include "sof-audio.h" #include "ops.h" -bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev) +/* + * helper to determine if there are only D0i3 compatible + * streams active + */ +bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev) +{ + struct snd_pcm_substream *substream; + struct snd_sof_pcm *spcm; + bool d0i3_compatible_active = false; + int dir; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_pcm_streams(dir) { + substream = spcm->stream[dir].substream; + if (!substream || !substream->runtime) + continue; + + /* + * substream->runtime being not NULL indicates that + * that the stream is open. No need to check the + * stream state. + */ + if (!spcm->stream[dir].d0i3_compatible) + return false; + + d0i3_compatible_active = true; + } + } + + return d0i3_compatible_active; +} +EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active); + +bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) { struct snd_sof_pcm *spcm; @@ -38,7 +71,14 @@ int sof_set_hw_params_upon_resume(struct device *dev) * have been suspended. */ list_for_each_entry(spcm, &sdev->pcm_list, list) { - for (dir = 0; dir <= SNDRV_PCM_STREAM_CAPTURE; dir++) { + for_each_pcm_streams(dir) { + /* + * do not reset hw_params upon resume for streams that + * were kept running during suspend + */ + if (spcm->stream[dir].suspend_ignored) + continue; + substream = spcm->stream[dir].substream; if (!substream || !substream->runtime) continue; @@ -279,16 +319,11 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, int dir; list_for_each_entry(spcm, &sdev->pcm_list, list) { - dir = SNDRV_PCM_STREAM_PLAYBACK; - if (spcm->stream[dir].comp_id == comp_id) { - *direction = dir; - return spcm; - } - - dir = SNDRV_PCM_STREAM_CAPTURE; - if (spcm->stream[dir].comp_id == comp_id) { - *direction = dir; - return spcm; + for_each_pcm_streams(dir) { + if (spcm->stream[dir].comp_id == comp_id) { + *direction = dir; + return spcm; + } } } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index a62fb2da6a6e..bf65f31af858 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -11,6 +11,8 @@ #ifndef __SOUND_SOC_SOF_AUDIO_H #define __SOUND_SOC_SOF_AUDIO_H +#include <linux/workqueue.h> + #include <sound/soc.h> #include <sound/control.h> #include <sound/sof/stream.h> /* needs to be included before control.h */ @@ -189,6 +191,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp, unsigned int pcm_id); void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); +void snd_sof_pcm_period_elapsed_work(struct work_struct *work); /* * Mixer IPC @@ -202,7 +205,8 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, /* PM */ int sof_restore_pipelines(struct device *dev); int sof_set_hw_params_upon_resume(struct device *dev); -bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev); +bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev); +bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev); /* Machine driver enumeration */ int sof_machine_register(struct snd_sof_dev *sdev, void *pdata); diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index 39ea8af6213f..16e49f2ee629 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -13,12 +13,21 @@ #include "ops.h" extern struct snd_sof_dsp_ops sof_imx8_ops; +extern struct snd_sof_dsp_ops sof_imx8x_ops; /* 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, @@ -103,6 +112,7 @@ static int sof_of_remove(struct platform_device *pdev) 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 { } }; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index bc2337cf1142..a4b297c842df 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -54,10 +54,26 @@ extern int sof_core_debug; (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) || \ IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)) -/* DSP D0ix sub-state */ -enum sof_d0_substate { - SOF_DSP_D0I0 = 0, /* DSP default D0 substate */ - SOF_DSP_D0I3, /* DSP D0i3(low power) substate*/ +/* DSP power state */ +enum sof_dsp_power_states { + SOF_DSP_PM_D0, + SOF_DSP_PM_D1, + SOF_DSP_PM_D2, + SOF_DSP_PM_D3_HOT, + SOF_DSP_PM_D3, + SOF_DSP_PM_D3_COLD, +}; + +struct sof_dsp_power_state { + u32 state; + u32 substate; /* platform-specific */ +}; + +/* System suspend target state */ +enum sof_system_suspend_state { + SOF_SUSPEND_NONE = 0, + SOF_SUSPEND_S0IX, + SOF_SUSPEND_S3, }; struct snd_sof_dev; @@ -154,6 +170,27 @@ struct snd_sof_dsp_ops { snd_pcm_uframes_t (*pcm_pointer)(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, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai); /* mandatory */ + int (*probe_free)(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai); /* mandatory */ + int (*probe_set_params)(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai); /* mandatory */ + int (*probe_trigger)(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai); /* mandatory */ + int (*probe_pointer)(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai); /* optional */ +#endif + /* host read DSP stream data */ void (*ipc_msg_data)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, @@ -169,14 +206,15 @@ struct snd_sof_dsp_ops { int (*post_fw_run)(struct snd_sof_dev *sof_dev); /* optional */ /* DSP PM */ - int (*suspend)(struct snd_sof_dev *sof_dev); /* optional */ + int (*suspend)(struct snd_sof_dev *sof_dev, + u32 target_state); /* optional */ int (*resume)(struct snd_sof_dev *sof_dev); /* optional */ int (*runtime_suspend)(struct snd_sof_dev *sof_dev); /* optional */ int (*runtime_resume)(struct snd_sof_dev *sof_dev); /* optional */ int (*runtime_idle)(struct snd_sof_dev *sof_dev); /* optional */ int (*set_hw_params_upon_resume)(struct snd_sof_dev *sdev); /* optional */ int (*set_power_state)(struct snd_sof_dev *sdev, - enum sof_d0_substate d0_substate); /* optional */ + const struct sof_dsp_power_state *target_state); /* optional */ /* DSP clocking */ int (*set_clk)(struct snd_sof_dev *sof_dev, u32 freq); /* optional */ @@ -323,10 +361,11 @@ struct snd_sof_dev { */ struct snd_soc_component_driver plat_drv; - /* power states related */ - enum sof_d0_substate d0_substate; - /* flag to track if the intended power target of suspend is S0ix */ - bool s0_suspend; + /* current DSP power state */ + struct sof_dsp_power_state dsp_power_state; + + /* Intended power target of system suspend */ + enum sof_system_suspend_state system_suspend_target; /* DSP firmware boot */ wait_queue_head_t boot_wait; @@ -376,16 +415,15 @@ struct snd_sof_dev { u32 enabled_cores_mask; /* keep track of enabled cores */ /* FW configuration */ - struct sof_ipc_dma_buffer_data *info_buffer; struct sof_ipc_window *info_window; /* IPC timeouts in ms */ int ipc_timeout; int boot_timeout; - /* Wait queue for code loading */ - wait_queue_head_t waitq; - int code_loading; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) + unsigned int extractor_stream_tag; +#endif /* DMA for Trace */ struct snd_dma_buffer dmatb; @@ -417,8 +455,6 @@ int snd_sof_resume(struct device *dev); int snd_sof_suspend(struct device *dev); int snd_sof_prepare(struct device *dev); void snd_sof_complete(struct device *dev); -int snd_sof_set_d0_substate(struct snd_sof_dev *sdev, - enum sof_d0_substate d0_substate); void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); @@ -454,6 +490,9 @@ int snd_sof_ipc_valid(struct snd_sof_dev *sdev); int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes); +int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, + void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes); /* * Trace/debug diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 9f4f8868b386..fe8ba3e05e08 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -9,6 +9,7 @@ // #include <linux/firmware.h> +#include <linux/workqueue.h> #include <sound/tlv.h> #include <sound/pcm_params.h> #include <uapi/sound/sof/tokens.h> @@ -1240,6 +1241,8 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, { struct snd_soc_card *card = scomp->card; struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *cpu_dai; + int i; list_for_each_entry(rtd, &card->rtd_list, list) { dev_vdbg(scomp->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", @@ -1254,13 +1257,15 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, switch (w->id) { case snd_soc_dapm_dai_out: - rtd->cpu_dai->capture_widget = w; + for_each_rtd_cpu_dais(rtd, i, cpu_dai) + cpu_dai->capture_widget = w; dai->name = rtd->dai_link->name; dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", w->name, rtd->dai_link->name); break; case snd_soc_dapm_dai_in: - rtd->cpu_dai->playback_widget = w; + for_each_rtd_cpu_dais(rtd, i, cpu_dai) + cpu_dai->playback_widget = w; dai->name = rtd->dai_link->name; dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", w->name, rtd->dai_link->name); @@ -2444,7 +2449,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_stream_caps *caps; struct snd_soc_tplg_private *private = &pcm->priv; struct snd_sof_pcm *spcm; - int stream = SNDRV_PCM_STREAM_PLAYBACK; + int stream; int ret = 0; /* nothing to do for BEs atm */ @@ -2456,8 +2461,12 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, return -ENOMEM; spcm->scomp = scomp; - spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = COMP_ID_UNASSIGNED; - spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = COMP_ID_UNASSIGNED; + + for_each_pcm_streams(stream) { + spcm->stream[stream].comp_id = COMP_ID_UNASSIGNED; + INIT_WORK(&spcm->stream[stream].period_elapsed_work, + snd_sof_pcm_period_elapsed_work); + } spcm->pcm = *pcm; dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name); @@ -2478,8 +2487,10 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, if (!spcm->pcm.playback) goto capture; + stream = SNDRV_PCM_STREAM_PLAYBACK; + dev_vdbg(scomp->dev, "tplg: pcm %s stream tokens: playback d0i3:%d\n", - spcm->pcm.pcm_name, spcm->stream[0].d0i3_compatible); + spcm->pcm.pcm_name, spcm->stream[stream].d0i3_compatible); caps = &spcm->pcm.caps[stream]; @@ -2509,7 +2520,7 @@ capture: return ret; dev_vdbg(scomp->dev, "tplg: pcm %s stream tokens: capture d0i3:%d\n", - spcm->pcm.pcm_name, spcm->stream[1].d0i3_compatible); + spcm->pcm.pcm_name, spcm->stream[stream].d0i3_compatible); caps = &spcm->pcm.caps[stream]; diff --git a/sound/soc/sprd/Kconfig b/sound/soc/sprd/Kconfig index 5474fd3de8c0..5e0ac8278572 100644 --- a/sound/soc/sprd/Kconfig +++ b/sound/soc/sprd/Kconfig @@ -8,7 +8,7 @@ config SND_SOC_SPRD the Spreadtrum SoCs' Audio interfaces. config SND_SOC_SPRD_MCDT - bool "Spreadtrum multi-channel data transfer support" + tristate "Spreadtrum multi-channel data transfer support" depends on SND_SOC_SPRD help Say y here to enable multi-channel data transfer support. It diff --git a/sound/soc/sprd/sprd-mcdt.h b/sound/soc/sprd/sprd-mcdt.h index 9cc7e207ac76..679e3af3baad 100644 --- a/sound/soc/sprd/sprd-mcdt.h +++ b/sound/soc/sprd/sprd-mcdt.h @@ -48,7 +48,7 @@ struct sprd_mcdt_chan { struct list_head list; }; -#ifdef CONFIG_SND_SOC_SPRD_MCDT +#if IS_ENABLED(CONFIG_SND_SOC_SPRD_MCDT) struct sprd_mcdt_chan *sprd_mcdt_request_chan(u8 channel, enum sprd_mcdt_channel_type type); void sprd_mcdt_free_chan(struct sprd_mcdt_chan *chan); diff --git a/sound/soc/sprd/sprd-pcm-compress.c b/sound/soc/sprd/sprd-pcm-compress.c index 6cddf551bc11..74d48340cade 100644 --- a/sound/soc/sprd/sprd-pcm-compress.c +++ b/sound/soc/sprd/sprd-pcm-compress.c @@ -135,7 +135,7 @@ static int sprd_platform_compr_dma_config(struct snd_compr_stream *cstream, struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; - struct sprd_compr_data *data = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct sprd_compr_data *data = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct sprd_pcm_dma_params *dma_params = data->dma_params; struct sprd_compr_dma *dma = &stream->dma[channel]; struct dma_slave_config config = { }; @@ -321,7 +321,7 @@ static int sprd_platform_compr_open(struct snd_compr_stream *cstream) struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; - struct sprd_compr_data *data = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct sprd_compr_data *data = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct sprd_compr_stream *stream; struct sprd_compr_callback cb; int stream_id = cstream->direction, ret; diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c index 2284558684bc..d12d3cad8cbd 100644 --- a/sound/soc/sprd/sprd-pcm-dma.c +++ b/sound/soc/sprd/sprd-pcm-dma.c @@ -200,7 +200,7 @@ static int sprd_pcm_hw_params(struct snd_soc_component *component, unsigned long flags; int ret, i, j, sg_num; - dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); if (!dma_params) { dev_warn(component->dev, "no dma parameters setting\n"); dma_private->params = NULL; diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 51407a21c440..16ff02953015 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -215,7 +215,7 @@ static int stm32_adfsdm_trigger(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(rtd->cpu_dai); + snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -235,7 +235,7 @@ static int stm32_adfsdm_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); int ret; ret = snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw); @@ -250,7 +250,7 @@ static int stm32_adfsdm_pcm_close(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(rtd->cpu_dai); + snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); priv->substream = NULL; @@ -263,7 +263,7 @@ static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer( { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(rtd->cpu_dai); + snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); return bytes_to_frames(substream->runtime, priv->pos); } @@ -274,7 +274,7 @@ static int stm32_adfsdm_pcm_hw_params(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(rtd->cpu_dai); + snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); priv->pcm_buff = substream->runtime->dma_area; @@ -287,7 +287,7 @@ static int stm32_adfsdm_pcm_new(struct snd_soc_component *component, { struct snd_pcm *pcm = rtd->pcm; struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(rtd->cpu_dai); + snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 3e7226a53e53..7c4d63c33f15 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -831,25 +831,33 @@ 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)) { - dev_err(&pdev->dev, "Could not get pclk\n"); + 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); } i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk"); if (IS_ERR(i2s->i2sclk)) { - dev_err(&pdev->dev, "Could not get i2sclk\n"); + 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); } i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k"); if (IS_ERR(i2s->x8kclk)) { - dev_err(&pdev->dev, "missing x8k parent clock\n"); + 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); } i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k"); if (IS_ERR(i2s->x11kclk)) { - dev_err(&pdev->dev, "missing x11k parent clock\n"); + 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); } @@ -866,12 +874,24 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, } /* Reset */ - rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (!IS_ERR(rst)) { - reset_control_assert(rst); - udelay(2); - reset_control_deassert(rst); + 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); } + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); + + return 0; +} + +static int stm32_i2s_remove(struct platform_device *pdev) +{ + snd_dmaengine_pcm_unregister(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); return 0; } @@ -903,42 +923,51 @@ 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)) { - dev_err(&pdev->dev, "regmap init failed\n"); + 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); } - ret = devm_snd_soc_register_component(&pdev->dev, &stm32_i2s_component, - i2s->dai_drv, 1); - if (ret) + 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; + } - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, - &stm32_i2s_pcm_config, 0); - if (ret) + ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, + i2s->dai_drv, 1); + if (ret) { + snd_dmaengine_pcm_unregister(&pdev->dev); return ret; + } /* Set SPI/I2S in i2s mode */ ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); if (ret) - return ret; + goto error; ret = regmap_read(i2s->regmap, STM32_I2S_IPIDR_REG, &val); if (ret) - return ret; + goto error; if (val == I2S_IPIDR_NUMBER) { ret = regmap_read(i2s->regmap, STM32_I2S_HWCFGR_REG, &val); if (ret) - return ret; + goto error; if (!FIELD_GET(I2S_HWCFGR_I2S_SUPPORT_MASK, val)) { dev_err(&pdev->dev, "Device does not support i2s mode\n"); - return -EPERM; + ret = -EPERM; + goto error; } ret = regmap_read(i2s->regmap, STM32_I2S_VERR_REG, &val); + if (ret) + goto error; dev_dbg(&pdev->dev, "I2S version: %lu.%lu registered\n", FIELD_GET(I2S_VERR_MAJ_MASK, val), @@ -946,6 +975,11 @@ static int stm32_i2s_probe(struct platform_device *pdev) } return ret; + +error: + stm32_i2s_remove(pdev); + + return ret; } MODULE_DEVICE_TABLE(of, stm32_i2s_ids); @@ -981,6 +1015,7 @@ static struct platform_driver stm32_i2s_driver = { .pm = &stm32_i2s_pm_ops, }, .probe = stm32_i2s_probe, + .remove = stm32_i2s_remove, }; module_platform_driver(stm32_i2s_driver); diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index e20267504b16..058757c721f0 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -174,20 +174,26 @@ 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)) { - dev_err(&pdev->dev, "missing bus clock pclk\n"); + 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); } } sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); if (IS_ERR(sai->clk_x8k)) { - dev_err(&pdev->dev, "missing x8k parent clock\n"); + 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); } sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k"); if (IS_ERR(sai->clk_x11k)) { - dev_err(&pdev->dev, "missing x11k parent clock\n"); + 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); } @@ -197,12 +203,16 @@ static int stm32_sai_probe(struct platform_device *pdev) return sai->irq; /* reset */ - rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (!IS_ERR(rst)) { - reset_control_assert(rst); - udelay(2); - reset_control_deassert(rst); + 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); } + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); /* Enable peripheral clock to allow register access */ ret = clk_prepare_enable(sai->pclk); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 10eb4b8e8e7e..2bd280c01c33 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1238,7 +1238,7 @@ static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); int *ptr = (int *)(runtime->dma_area + hwoff + channel * (runtime->dma_bytes / runtime->channels)); @@ -1380,7 +1380,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)) { - dev_err(&pdev->dev, "Failed to initialize MMIO\n"); + 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); } @@ -1471,7 +1473,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)) { - dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); + 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); } @@ -1545,7 +1549,8 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); if (ret) { - dev_err(&pdev->dev, "Could not register pcm dma\n"); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Could not register pcm dma\n"); return ret; } diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 3769d9ce5dbe..1bfa3b2ba974 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -406,7 +406,9 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); if (IS_ERR(spdifrx->ctrl_chan)) { - dev_err(dev, "dma_request_slave_channel failed\n"); + 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); } @@ -929,7 +931,9 @@ static int stm32_spdifrx_parse_of(struct platform_device *pdev, spdifrx->kclk = devm_clk_get(&pdev->dev, "kclk"); if (IS_ERR(spdifrx->kclk)) { - dev_err(&pdev->dev, "Could not get kclk\n"); + 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); } @@ -940,6 +944,22 @@ static int stm32_spdifrx_parse_of(struct platform_device *pdev, return 0; } +static int stm32_spdifrx_remove(struct platform_device *pdev) +{ + struct stm32_spdifrx_data *spdifrx = platform_get_drvdata(pdev); + + if (spdifrx->ctrl_chan) + dma_release_channel(spdifrx->ctrl_chan); + + if (spdifrx->dmab) + snd_dma_free_pages(spdifrx->dmab); + + snd_dmaengine_pcm_unregister(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + + return 0; +} + static int stm32_spdifrx_probe(struct platform_device *pdev) { struct stm32_spdifrx_data *spdifrx; @@ -967,7 +987,9 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) spdifrx->base, spdifrx->regmap_conf); if (IS_ERR(spdifrx->regmap)) { - dev_err(&pdev->dev, "Regmap init failed\n"); + 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); } @@ -978,37 +1000,46 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) return ret; } - rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (!IS_ERR(rst)) { - reset_control_assert(rst); - udelay(2); - reset_control_deassert(rst); + 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); } + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); - ret = devm_snd_soc_register_component(&pdev->dev, - &stm32_spdifrx_component, - stm32_spdifrx_dai, - ARRAY_SIZE(stm32_spdifrx_dai)); - if (ret) + 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; + } + + ret = snd_soc_register_component(&pdev->dev, + &stm32_spdifrx_component, + stm32_spdifrx_dai, + ARRAY_SIZE(stm32_spdifrx_dai)); + if (ret) { + snd_dmaengine_pcm_unregister(&pdev->dev); + return ret; + } ret = stm32_spdifrx_dma_ctrl_register(&pdev->dev, spdifrx); if (ret) goto error; - pcm_config = &stm32_spdifrx_pcm_config; - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); - if (ret) { - dev_err(&pdev->dev, "PCM DMA register returned %d\n", ret); - goto error; - } - ret = regmap_read(spdifrx->regmap, STM32_SPDIFRX_IDR, &idr); if (ret) goto error; if (idr == SPDIFRX_IPIDR_NUMBER) { ret = regmap_read(spdifrx->regmap, STM32_SPDIFRX_VERR, &ver); + if (ret) + goto error; dev_dbg(&pdev->dev, "SPDIFRX version: %lu.%lu registered\n", FIELD_GET(SPDIFRX_VERR_MAJ_MASK, ver), @@ -1018,27 +1049,11 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) return ret; error: - if (!IS_ERR(spdifrx->ctrl_chan)) - dma_release_channel(spdifrx->ctrl_chan); - if (spdifrx->dmab) - snd_dma_free_pages(spdifrx->dmab); + stm32_spdifrx_remove(pdev); return ret; } -static int stm32_spdifrx_remove(struct platform_device *pdev) -{ - struct stm32_spdifrx_data *spdifrx = platform_get_drvdata(pdev); - - if (spdifrx->ctrl_chan) - dma_release_channel(spdifrx->ctrl_chan); - - if (spdifrx->dmab) - snd_dma_free_pages(spdifrx->dmab); - - return 0; -} - MODULE_DEVICE_TABLE(of, stm32_spdifrx_ids); #ifdef CONFIG_PM_SLEEP diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index 98a9fe645521..86779a99df75 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -244,7 +244,7 @@ static int sun4i_spdif_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -EINVAL; diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 686561df8e13..ca51af114419 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -86,7 +86,6 @@ #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9) struct sun8i_codec { - struct device *dev; struct regmap *regmap; struct clk *clk_module; struct clk *clk_bus; @@ -542,8 +541,6 @@ static int sun8i_codec_probe(struct platform_device *pdev) if (!scodec) return -ENOMEM; - scodec->dev = &pdev->dev; - scodec->clk_module = devm_clk_get(&pdev->dev, "mod"); if (IS_ERR(scodec->clk_module)) { dev_err(&pdev->dev, "Failed to get the module clock\n"); diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 9e8b1497efd3..ec39ecba1e8b 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -37,7 +37,7 @@ static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card); int srate, mclk; diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c index 4954a33ff46b..d800b62b36f8 100644 --- a/sound/soc/tegra/tegra_max98090.c +++ b/sound/soc/tegra/tegra_max98090.c @@ -38,7 +38,7 @@ static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card); int srate, mclk; diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index d46915a3ec4c..9878bc3eb89e 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -40,7 +40,7 @@ static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); int srate, mclk; diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c index 81cb6cc6236e..5821313db977 100644 --- a/sound/soc/tegra/tegra_rt5677.c +++ b/sound/soc/tegra/tegra_rt5677.c @@ -42,7 +42,7 @@ static int tegra_rt5677_asoc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); int srate, mclk, err; diff --git a/sound/soc/tegra/tegra_sgtl5000.c b/sound/soc/tegra/tegra_sgtl5000.c index e13b81d29cf3..dc411ba2e36d 100644 --- a/sound/soc/tegra/tegra_sgtl5000.c +++ b/sound/soc/tegra/tegra_sgtl5000.c @@ -36,7 +36,7 @@ static int tegra_sgtl5000_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct tegra_sgtl5000 *machine = snd_soc_card_get_drvdata(card); int srate, mclk; diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index f6dd790dad71..0d653a605358 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c @@ -40,7 +40,7 @@ static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card); int srate, mclk; diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index f08d3489c3cf..9b5651502f12 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -45,7 +45,7 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); int srate, mclk; @@ -143,19 +143,37 @@ static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w, return 0; } +static int tegra_wm8903_event_int_mic(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 tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); + + if (!gpio_is_valid(machine->gpio_int_mic_en)) + return 0; + + gpio_set_value_cansleep(machine->gpio_int_mic_en, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + static const struct snd_soc_dapm_widget tegra_wm8903_dapm_widgets[] = { SND_SOC_DAPM_SPK("Int Spk", tegra_wm8903_event_int_spk), SND_SOC_DAPM_HP("Headphone Jack", tegra_wm8903_event_hp), SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("Int Mic", tegra_wm8903_event_int_mic), }; static const struct snd_kcontrol_new tegra_wm8903_controls[] = { SOC_DAPM_PIN_SWITCH("Int Spk"), + SOC_DAPM_PIN_SWITCH("Int Mic"), }; static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = codec_dai->component; struct snd_soc_card *card = rtd->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); @@ -187,7 +205,7 @@ static int tegra_wm8903_remove(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = codec_dai->component; wm8903_mic_detect(component, NULL, 0, 0); diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 3f67ddd13674..f9834afaa2e8 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -35,7 +35,7 @@ static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card); int srate, mclk; diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig index 29f61053ab62..c5408c129f34 100644 --- a/sound/soc/ti/Kconfig +++ b/sound/soc/ti/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "Audio support for Texas Instruments SoCs" -depends on DMA_OMAP || TI_EDMA || COMPILE_TEST +depends on DMA_OMAP || TI_EDMA || TI_K3_UDMA || COMPILE_TEST config SND_SOC_TI_EDMA_PCM tristate @@ -10,6 +10,10 @@ config SND_SOC_TI_SDMA_PCM tristate select SND_SOC_GENERIC_DMAENGINE_PCM +config SND_SOC_TI_UDMA_PCM + tristate + select SND_SOC_GENERIC_DMAENGINE_PCM + comment "Texas Instruments DAI support for:" config SND_SOC_DAVINCI_ASP tristate "daVinci Audio Serial Port (ASP) or McBSP support" @@ -24,6 +28,7 @@ config SND_SOC_DAVINCI_MCASP tristate "Multichannel Audio Serial Port (McASP) support" select SND_SOC_TI_EDMA_PCM select SND_SOC_TI_SDMA_PCM + select SND_SOC_TI_UDMA_PCM help Say Y or M here if you want to have support for McASP IP found in various Texas Instruments SoCs like: @@ -31,6 +36,7 @@ config SND_SOC_DAVINCI_MCASP - Sitara line of SoCs (AM335x, AM438x, etc) - DRA7x devices - Keystone devices + - K3 devices (am654, j721e) config SND_SOC_DAVINCI_VCIF tristate "daVinci Voice Interface (VCIF) support" diff --git a/sound/soc/ti/Makefile b/sound/soc/ti/Makefile index 08c44d56ef3e..ea48c6679cc7 100644 --- a/sound/soc/ti/Makefile +++ b/sound/soc/ti/Makefile @@ -3,9 +3,11 @@ # Platform drivers snd-soc-ti-edma-objs := edma-pcm.o snd-soc-ti-sdma-objs := sdma-pcm.o +snd-soc-ti-udma-objs := udma-pcm.o obj-$(CONFIG_SND_SOC_TI_EDMA_PCM) += snd-soc-ti-edma.o obj-$(CONFIG_SND_SOC_TI_SDMA_PCM) += snd-soc-ti-sdma.o +obj-$(CONFIG_SND_SOC_TI_UDMA_PCM) += snd-soc-ti-udma.o # CPU DAI drivers snd-soc-davinci-asp-objs := davinci-i2s.o diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c index 8e2fb81ad05c..e17cd5e939f0 100644 --- a/sound/soc/ti/ams-delta.c +++ b/sound/soc/ti/ams-delta.c @@ -460,14 +460,14 @@ static void ams_delta_shutdown(struct snd_pcm_substream *substream) static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct snd_soc_dapm_context *dapm = &card->dapm; int ret; /* Codec is ready, now add/activate board specific controls */ /* Store a pointer to the codec structure for tty ldisc use */ - cx20442_codec = rtd->codec_dai->component; + cx20442_codec = asoc_rtd_to_codec(rtd, 0)->component; /* Add hook switch - can be used to control the codec from userspace * even if line discipline fails */ diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c index 686b23d7a90d..2cfbeebdfb41 100644 --- a/sound/soc/ti/davinci-evm.c +++ b/sound/soc/ti/davinci-evm.c @@ -54,8 +54,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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 snd_soc_card *soc_card = rtd->card; int ret = 0; unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index e1e937eb1dc1..734ffe925c4d 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -38,6 +38,7 @@ #include "edma-pcm.h" #include "sdma-pcm.h" +#include "udma-pcm.h" #include "davinci-mcasp.h" #define MCASP_MAX_AFIFO_DEPTH 64 @@ -1764,10 +1765,8 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( } else if (match) { pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - ret = -ENOMEM; - return pdata; - } + if (!pdata) + return NULL; } else { /* control shouldn't reach here. something is wrong */ ret = -EINVAL; @@ -1875,6 +1874,7 @@ nodata: enum { PCM_EDMA, PCM_SDMA, + PCM_UDMA, }; static const char *sdma_prefix = "ti,omap"; @@ -1912,6 +1912,8 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) dev_dbg(mcasp->dev, "DMA controller compatible = \"%s\"\n", tmp); if (!strncmp(tmp, sdma_prefix, strlen(sdma_prefix))) return PCM_SDMA; + else if (strstr(tmp, "udmap")) + return PCM_UDMA; return PCM_EDMA; } @@ -2371,6 +2373,9 @@ static int davinci_mcasp_probe(struct platform_device *pdev) case PCM_SDMA: ret = sdma_pcm_platform_register(&pdev->dev, "tx", "rx"); break; + case PCM_UDMA: + ret = udma_pcm_platform_register(&pdev->dev); + break; default: dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret); case -EPROBE_DEFER: diff --git a/sound/soc/ti/davinci-vcif.c b/sound/soc/ti/davinci-vcif.c index c84650e4a7aa..ee4d3ef821a1 100644 --- a/sound/soc/ti/davinci-vcif.c +++ b/sound/soc/ti/davinci-vcif.c @@ -43,7 +43,7 @@ static void davinci_vcif_start(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct davinci_vcif_dev *davinci_vcif_dev = - snd_soc_dai_get_drvdata(rtd->cpu_dai); + snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; u32 w; @@ -62,7 +62,7 @@ static void davinci_vcif_stop(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct davinci_vcif_dev *davinci_vcif_dev = - snd_soc_dai_get_drvdata(rtd->cpu_dai); + snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; u32 w; diff --git a/sound/soc/ti/n810.c b/sound/soc/ti/n810.c index 3ad2b6daf31e..a1672b479cb7 100644 --- a/sound/soc/ti/n810.c +++ b/sound/soc/ti/n810.c @@ -101,7 +101,7 @@ static int n810_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int err; /* Set the codec system clock for DAC and ADC */ diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c index 6d564ab5e437..61e45fea5dd8 100644 --- a/sound/soc/ti/omap-abe-twl6040.c +++ b/sound/soc/ti/omap-abe-twl6040.c @@ -46,7 +46,7 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); int clk_id, freq; @@ -78,7 +78,7 @@ static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret = 0; ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_SYSCLK_PAD_CLKS, @@ -166,7 +166,7 @@ static const struct snd_soc_dapm_route audio_map[] = { static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_card *card = rtd->card; struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); int hs_trim; diff --git a/sound/soc/ti/omap-mcbsp-st.c b/sound/soc/ti/omap-mcbsp-st.c index 1a3fe854e856..5a32b54bbf3b 100644 --- a/sound/soc/ti/omap-mcbsp-st.c +++ b/sound/soc/ti/omap-mcbsp-st.c @@ -489,7 +489,7 @@ OMAP_MCBSP_ST_CONTROLS(3); int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); if (!mcbsp->st_data) { diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c index 302d5c493c29..3d41ca2238d4 100644 --- a/sound/soc/ti/omap-mcbsp.c +++ b/sound/soc/ti/omap-mcbsp.c @@ -737,7 +737,7 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream, unsigned int packet_size) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); int words; @@ -902,7 +902,7 @@ static snd_pcm_sframes_t omap_mcbsp_dai_delay( struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); u16 fifo_use; snd_pcm_sframes_t delay; diff --git a/sound/soc/ti/omap-mcpdm.c b/sound/soc/ti/omap-mcpdm.c index d7ac4df6f2d9..f2dbadea33bb 100644 --- a/sound/soc/ti/omap-mcpdm.c +++ b/sound/soc/ti/omap-mcpdm.c @@ -532,7 +532,7 @@ static const struct snd_soc_component_driver omap_mcpdm_component = { void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd, u8 rx1, u8 rx2) { - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); mcpdm->dn_rx_offset = MCPDM_DNOFST_RX1(rx1) | MCPDM_DNOFST_RX2(rx2); } diff --git a/sound/soc/ti/omap3pandora.c b/sound/soc/ti/omap3pandora.c index 545f8dac9bd5..b04146311b31 100644 --- a/sound/soc/ti/omap3pandora.c +++ b/sound/soc/ti/omap3pandora.c @@ -32,8 +32,8 @@ static int omap3pandora_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret; /* Set the codec system clock for DAC and ADC */ diff --git a/sound/soc/ti/osk5912.c b/sound/soc/ti/osk5912.c index 1ca466bc4025..e01485cc51a1 100644 --- a/sound/soc/ti/osk5912.c +++ b/sound/soc/ti/osk5912.c @@ -39,7 +39,7 @@ static int osk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int err; /* Set the codec system clock for DAC and ADC */ diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c index fdb0dc85fe67..2a714a004163 100644 --- a/sound/soc/ti/rx51.c +++ b/sound/soc/ti/rx51.c @@ -103,7 +103,7 @@ static int rx51_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); /* Set the codec system clock for DAC and ADC */ return snd_soc_dai_set_sysclk(codec_dai, 0, 19200000, diff --git a/sound/soc/ti/udma-pcm.c b/sound/soc/ti/udma-pcm.c new file mode 100644 index 000000000000..39830caaaf7c --- /dev/null +++ b/sound/soc/ti/udma-pcm.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com + * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> + */ + +#include <linux/module.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +#include "udma-pcm.h" + +static const struct snd_pcm_hardware udma_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_INTERLEAVED, + .buffer_bytes_max = SIZE_MAX, + .period_bytes_min = 32, + .period_bytes_max = SZ_64K, + .periods_min = 2, + .periods_max = UINT_MAX, +}; + +static const struct snd_dmaengine_pcm_config udma_dmaengine_pcm_config = { + .pcm_hardware = &udma_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + +int udma_pcm_platform_register(struct device *dev) +{ + return devm_snd_dmaengine_pcm_register(dev, &udma_dmaengine_pcm_config, + 0); +} +EXPORT_SYMBOL_GPL(udma_pcm_platform_register); + +MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); +MODULE_DESCRIPTION("UDMA PCM ASoC platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/ti/udma-pcm.h b/sound/soc/ti/udma-pcm.h new file mode 100644 index 000000000000..54111e7312c1 --- /dev/null +++ b/sound/soc/ti/udma-pcm.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + */ + +#ifndef __UDMA_PCM_H__ +#define __UDMA_PCM_H__ + +#if IS_ENABLED(CONFIG_SND_SOC_TI_UDMA_PCM) +int udma_pcm_platform_register(struct device *dev); +#else +static inline int udma_pcm_platform_register(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_TI_UDMA_PCM */ + +#endif /* __UDMA_PCM_H__ */ diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 985487cc3a55..4b1cd4da3e36 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -269,7 +269,7 @@ static int txx9aclc_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; - struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); struct snd_pcm *pcm = rtd->pcm; struct platform_device *pdev = to_platform_device(component->dev); struct txx9aclc_soc_device *dev; diff --git a/sound/soc/uniphier/aio-compress.c b/sound/soc/uniphier/aio-compress.c index 17f773ac5ca1..232d3cc5bce0 100644 --- a/sound/soc/uniphier/aio-compress.c +++ b/sound/soc/uniphier/aio-compress.c @@ -23,7 +23,7 @@ static int uniphier_aio_comprdma_new(struct snd_soc_pcm_runtime *rtd) { struct snd_compr *compr = rtd->compr; struct device *dev = compr->card->dev; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[compr->direction]; size_t size = AUD_RING_SIZE; int dma_dir = DMA_FROM_DEVICE, ret; @@ -56,7 +56,7 @@ static int uniphier_aio_comprdma_free(struct snd_soc_pcm_runtime *rtd) { struct snd_compr *compr = rtd->compr; struct device *dev = compr->card->dev; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[compr->direction]; int dma_dir = DMA_FROM_DEVICE; @@ -73,7 +73,7 @@ static int uniphier_aio_comprdma_free(struct snd_soc_pcm_runtime *rtd) static int uniphier_aio_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; int ret; @@ -98,7 +98,7 @@ static int uniphier_aio_compr_open(struct snd_compr_stream *cstream) static int uniphier_aio_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; int ret; @@ -118,7 +118,7 @@ static int uniphier_aio_compr_get_params(struct snd_compr_stream *cstream, struct snd_codec *params) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; *params = sub->cparams.codec; @@ -130,7 +130,7 @@ static int uniphier_aio_compr_set_params(struct snd_compr_stream *cstream, struct snd_compr_params *params) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; struct device *dev = &aio->chip->pdev->dev; int ret; @@ -165,7 +165,7 @@ static int uniphier_aio_compr_set_params(struct snd_compr_stream *cstream, static int uniphier_aio_compr_hw_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; sub->setting = 0; @@ -177,7 +177,7 @@ static int uniphier_aio_compr_prepare(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_compr_runtime *runtime = cstream->runtime; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; int bytes = runtime->fragment_size; unsigned long flags; @@ -215,7 +215,7 @@ static int uniphier_aio_compr_trigger(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_compr_runtime *runtime = cstream->runtime; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; struct device *dev = &aio->chip->pdev->dev; int bytes = runtime->fragment_size, ret = 0; @@ -248,7 +248,7 @@ static int uniphier_aio_compr_pointer(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_compr_runtime *runtime = cstream->runtime; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; int bytes = runtime->fragment_size; unsigned long flags; @@ -322,7 +322,7 @@ static int uniphier_aio_compr_copy(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_compr_runtime *runtime = cstream->runtime; struct device *carddev = rtd->compr->card->dev; - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; size_t cnt = min_t(size_t, count, aio_rb_space_to_end(sub) / 2); int bytes = runtime->fragment_size; diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c index da83423c52e2..4bbcb007df41 100644 --- a/sound/soc/uniphier/aio-dma.c +++ b/sound/soc/uniphier/aio-dma.c @@ -109,7 +109,7 @@ static int uniphier_aiodma_prepare(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; int bytes = runtime->period_size * runtime->channels * samples_to_bytes(runtime, 1); @@ -136,7 +136,7 @@ static int uniphier_aiodma_trigger(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; struct device *dev = &aio->chip->pdev->dev; int bytes = runtime->period_size * @@ -172,7 +172,7 @@ static snd_pcm_uframes_t uniphier_aiodma_pointer( { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; int bytes = runtime->period_size * runtime->channels * samples_to_bytes(runtime, 1); diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 77655084bbde..6aaa19829a73 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -215,8 +215,8 @@ static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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 device *dev = rtd->card->dev; unsigned int fmt; int channels, ret = 0, driver_mode, slots; @@ -339,7 +339,7 @@ static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, static int mop500_ab8500_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); mutex_lock(&mop500_ab8500_params_lock); __clear_bit(cpu_dai->id, &mop500_ab8500_usage); diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index 9445dbe8e039..39b96c132bc8 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -46,7 +46,7 @@ static const struct snd_pcm_hardware ux500_pcm_hw = { static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream) { - struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); u16 per_data_width, mem_data_width; struct stedma40_chan_cfg *dma_cfg; struct ux500_msp_dma_params *dma_params; @@ -86,7 +86,7 @@ static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream, struct dma_slave_config *slave_config) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct msp_i2s_platform_data *pdata = rtd->cpu_dai->dev->platform_data; + struct msp_i2s_platform_data *pdata = asoc_rtd_to_cpu(rtd, 0)->dev->platform_data; struct snd_dmaengine_dai_dma_data *snd_dma_params; struct ux500_msp_dma_params *ste_dma_params; dma_addr_t dma_addr; @@ -94,11 +94,11 @@ static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream, if (pdata) { ste_dma_params = - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); dma_addr = ste_dma_params->tx_rx_addr; } else { snd_dma_params = - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); dma_addr = snd_dma_params->addr; } diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c index bcf442faff7c..68af2176b19c 100644 --- a/sound/soc/xtensa/xtfpga-i2s.c +++ b/sound/soc/xtensa/xtfpga-i2s.c @@ -373,7 +373,7 @@ static int xtfpga_pcm_open(struct snd_soc_component *component, void *p; snd_soc_set_runtime_hwparams(substream, &xtfpga_pcm_hardware); - p = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + p = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); runtime->private_data = p; return 0; diff --git a/sound/soc/zte/zx-spdif.c b/sound/soc/zte/zx-spdif.c index 60382ec23832..a3a07c0730e6 100644 --- a/sound/soc/zte/zx-spdif.c +++ b/sound/soc/zte/zx-spdif.c @@ -322,7 +322,6 @@ static int zx_spdif_probe(struct platform_device *pdev) zx_spdif->mapbase = res->start; zx_spdif->reg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(zx_spdif->reg_base)) { - dev_err(&pdev->dev, "ioremap failed!\n"); return PTR_ERR(zx_spdif->reg_base); } diff --git a/sound/soc/zte/zx-tdm.c b/sound/soc/zte/zx-tdm.c index 0e5a05b25a77..4f787185d630 100644 --- a/sound/soc/zte/zx-tdm.c +++ b/sound/soc/zte/zx-tdm.c @@ -371,7 +371,6 @@ static struct snd_soc_dai_driver zx_tdm_dai = { static int zx_tdm_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct of_phandle_args out_args; unsigned int dma_reg_offset; struct zx_tdm_info *zx_tdm; @@ -384,7 +383,7 @@ static int zx_tdm_probe(struct platform_device *pdev) if (!zx_tdm) return -ENOMEM; - zx_tdm->dev = dev; + zx_tdm->dev = &pdev->dev; zx_tdm->dai_wclk = devm_clk_get(&pdev->dev, "wclk"); if (IS_ERR(zx_tdm->dai_wclk)) { diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 78edd7d2f418..56031026b113 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -13,6 +13,7 @@ snd-usb-audio-objs := card.o \ mixer_scarlett.o \ mixer_scarlett_gen2.o \ mixer_us16x08.o \ + mixer_s1810c.o \ pcm.o \ power.o \ proc.o \ diff --git a/sound/usb/card.c b/sound/usb/card.c index 827fb0bc8b56..fd6fd1726ea0 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -72,6 +72,7 @@ static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; static bool autoclock = true; static char *quirk_alias[SNDRV_CARDS]; +static char *delayed_register[SNDRV_CARDS]; bool snd_usb_use_vmalloc = true; bool snd_usb_skip_validation; @@ -95,6 +96,8 @@ module_param(autoclock, bool, 0444); MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes)."); module_param_array(quirk_alias, charp, NULL, 0444); MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); +module_param_array(delayed_register, charp, NULL, 0444); +MODULE_PARM_DESC(delayed_register, "Quirk for delayed registration, given by id:iface, e.g. 0123abcd:4."); module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444); MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes)."); module_param_named(skip_validation, snd_usb_skip_validation, bool, 0444); @@ -525,6 +528,21 @@ static bool get_alias_id(struct usb_device *dev, unsigned int *id) return false; } +static bool check_delayed_register_option(struct snd_usb_audio *chip, int iface) +{ + int i; + unsigned int id, inum; + + for (i = 0; i < ARRAY_SIZE(delayed_register); i++) { + if (delayed_register[i] && + sscanf(delayed_register[i], "%x:%x", &id, &inum) == 2 && + id == chip->usb_id) + return inum != iface; + } + + return false; +} + static const struct usb_device_id usb_audio_ids[]; /* defined below */ /* look for the corresponding quirk */ @@ -662,10 +680,22 @@ static int usb_audio_probe(struct usb_interface *intf, goto __error; } - /* we are allowed to call snd_card_register() many times */ - err = snd_card_register(chip->card); - if (err < 0) - goto __error; + if (chip->need_delayed_register) { + dev_info(&dev->dev, + "Found post-registration device assignment: %08x:%02x\n", + chip->usb_id, ifnum); + chip->need_delayed_register = false; /* clear again */ + } + + /* we are allowed to call snd_card_register() many times, but first + * check to see if a device needs to skip it or do anything special + */ + if (!snd_usb_registration_quirk(chip, ifnum) && + !check_delayed_register_option(chip, ifnum)) { + err = snd_card_register(chip->card); + if (err < 0) + goto __error; + } if (quirk && quirk->shares_media_device) { /* don't want to fail when snd_media_device_create() fails */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index a48313dfa967..b118cf97607f 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -151,16 +151,15 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i return ret; } -/* - * Assume the clock is valid if clock source supports only one single sample - * rate, the terminal is connected directly to it (there is no clock selector) - * and clock type is internal. This is to deal with some Denon DJ controllers - * that always reports that clock is invalid. - */ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip, struct audioformat *fmt, int source_id) { + bool ret = false; + int count; + unsigned char data; + struct usb_device *dev = chip->dev; + if (fmt->protocol == UAC_VERSION_2) { struct uac_clock_source_descriptor *cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, source_id); @@ -168,13 +167,51 @@ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip, if (!cs_desc) return false; - return (fmt->nr_rates == 1 && - (fmt->clock & 0xff) == cs_desc->bClockID && - (cs_desc->bmAttributes & 0x3) != - UAC_CLOCK_SOURCE_TYPE_EXT); + /* + * Assume the clock is valid if clock source supports only one + * single sample rate, the terminal is connected directly to it + * (there is no clock selector) and clock type is internal. + * This is to deal with some Denon DJ controllers that always + * reports that clock is invalid. + */ + if (fmt->nr_rates == 1 && + (fmt->clock & 0xff) == cs_desc->bClockID && + (cs_desc->bmAttributes & 0x3) != + UAC_CLOCK_SOURCE_TYPE_EXT) + return true; + } + + /* + * MOTU MicroBook IIc + * Sample rate changes takes more than 2 seconds for this device. Clock + * validity request returns false during that period. + */ + if (chip->usb_id == USB_ID(0x07fd, 0x0004)) { + count = 0; + + while ((!ret) && (count < 50)) { + int err; + + msleep(100); + + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + UAC2_CS_CONTROL_CLOCK_VALID << 8, + snd_usb_ctrl_intf(chip) | (source_id << 8), + &data, sizeof(data)); + if (err < 0) { + dev_warn(&dev->dev, + "%s(): cannot get clock validity for id %d\n", + __func__, source_id); + return false; + } + + ret = !!data; + count++; + } } - return false; + return ret; } static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, diff --git a/sound/usb/format.c b/sound/usb/format.c index 9f5cb4ed3a0c..50e1874c847c 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -247,6 +247,36 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof return 0; } + +/* + * Presonus Studio 1810c supports a limited set of sampling + * rates per altsetting but reports the full set each time. + * If we don't filter out the unsupported rates and attempt + * to configure the card, it will hang refusing to do any + * further audio I/O until a hard reset is performed. + * + * The list of supported rates per altsetting (set of available + * I/O channels) is described in the owner's manual, section 2.2. + */ +static bool s1810c_valid_sample_rate(struct audioformat *fp, + unsigned int rate) +{ + switch (fp->altsetting) { + case 1: + /* All ADAT ports available */ + return rate <= 48000; + case 2: + /* Half of ADAT ports available */ + return (rate == 88200 || rate == 96000); + case 3: + /* Analog I/O only (no S/PDIF nor ADAT) */ + return rate >= 176400; + default: + return false; + } + return false; +} + /* * Helper function to walk the array of sample rate triplets reported by * the device. The problem is that we need to parse whole array first to @@ -283,6 +313,12 @@ 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) && + !s1810c_valid_sample_rate(fp, rate)) + goto skip_rate; + if (fp->rate_table) fp->rate_table[nr_rates] = rate; if (!fp->rate_min || rate < fp->rate_min) @@ -297,6 +333,7 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, break; } +skip_rate: /* avoid endless loop */ if (res == 0) break; diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 392e5fda680c..047b90595d65 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -91,7 +91,7 @@ struct usb_ms_endpoint_descriptor { __u8 bDescriptorType; __u8 bDescriptorSubtype; __u8 bNumEmbMIDIJack; - __u8 baAssocJackID[0]; + __u8 baAssocJackID[]; } __attribute__ ((packed)); struct snd_usb_midi_in_endpoint; @@ -1826,6 +1826,28 @@ static int snd_usbmidi_create_endpoints(struct snd_usb_midi *umidi, return 0; } +static struct usb_ms_endpoint_descriptor *find_usb_ms_endpoint_descriptor( + struct usb_host_endpoint *hostep) +{ + unsigned char *extra = hostep->extra; + int extralen = hostep->extralen; + + while (extralen > 3) { + struct usb_ms_endpoint_descriptor *ms_ep = + (struct usb_ms_endpoint_descriptor *)extra; + + if (ms_ep->bLength > 3 && + ms_ep->bDescriptorType == USB_DT_CS_ENDPOINT && + ms_ep->bDescriptorSubtype == UAC_MS_GENERAL) + return ms_ep; + if (!extra[0]) + break; + extralen -= extra[0]; + extra += extra[0]; + } + return NULL; +} + /* * Returns MIDIStreaming device capabilities. */ @@ -1863,11 +1885,8 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi *umidi, ep = get_ep_desc(hostep); if (!usb_endpoint_xfer_bulk(ep) && !usb_endpoint_xfer_int(ep)) continue; - ms_ep = (struct usb_ms_endpoint_descriptor *)hostep->extra; - if (hostep->extralen < 4 || - ms_ep->bLength < 4 || - ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT || - ms_ep->bDescriptorSubtype != UAC_MS_GENERAL) + ms_ep = find_usb_ms_endpoint_descriptor(hostep); + if (!ms_ep) continue; if (usb_endpoint_dir_out(ep)) { if (endpoints[epidx].out_ep) { diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 81b2db0edd5f..721d12130d0c 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -292,6 +292,11 @@ static int uac2_ctl_value_size(int val_type) * retrieve a mixer value */ +static inline int mixer_ctrl_intf(struct usb_mixer_interface *mixer) +{ + return get_iface_desc(mixer->hostif)->bInterfaceNumber; +} + static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { @@ -306,7 +311,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, return -EIO; while (timeout-- > 0) { - idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); + idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, idx, buf, val_len); @@ -354,7 +359,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, if (ret) goto error; - idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); + idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, idx, buf, size); @@ -479,7 +484,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, return -EIO; while (timeout-- > 0) { - idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); + idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, @@ -901,6 +906,12 @@ static int parse_term_effect_unit(struct mixer_build *state, struct usb_audio_term *term, void *p1, int id) { + struct uac2_effect_unit_descriptor *d = p1; + int err; + + err = __check_input_term(state, d->bSourceID, term); + if (err < 0) + return err; term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */ term->id = id; return 0; @@ -1203,7 +1214,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { usb_audio_err(cval->head.mixer->chip, "%d:%d: cannot get min/max values for control %d (id %d)\n", - cval->head.id, snd_usb_ctrl_intf(cval->head.mixer->chip), + cval->head.id, mixer_ctrl_intf(cval->head.mixer), cval->control, cval->head.id); return -EINVAL; } @@ -1422,7 +1433,7 @@ static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol, if (ret) goto error; - idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); + idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); if (cval->head.mixer->protocol == UAC_VERSION_2) { struct uac2_connectors_ctl_blk uac2_conn; @@ -1674,6 +1685,16 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, /* get min/max values */ get_min_max_with_quirks(cval, 0, kctl); + /* skip a bogus volume range */ + if (cval->max <= cval->min) { + usb_audio_dbg(mixer->chip, + "[%d] FU [%s] skipped due to invalid volume\n", + cval->head.id, kctl->id.name); + snd_ctl_free_one(kctl); + return; + } + + if (control == UAC_FU_VOLUME) { check_mapped_dB(map, cval); if (cval->dBmin < cval->dBmax || !cval->initialized) { @@ -3203,7 +3224,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, list_for_each_entry(mixer, &chip->mixer_list, list) { snd_iprintf(buffer, "USB Mixer: usb_id=0x%08x, ctrlif=%i, ctlerr=%i\n", - chip->usb_id, snd_usb_ctrl_intf(chip), + chip->usb_id, mixer_ctrl_intf(mixer), mixer->ignore_ctl_error); snd_iprintf(buffer, "Card: %s\n", chip->card->longname); for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index c237e24f08d9..02b036b2aefb 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -34,6 +34,7 @@ #include "mixer_scarlett.h" #include "mixer_scarlett_gen2.h" #include "mixer_us16x08.h" +#include "mixer_s1810c.h" #include "helper.h" struct std_mono_table { @@ -2277,6 +2278,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x2a39, 0x3fd4): /* RME */ err = snd_rme_controls_create(mixer); break; + + case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */ + err = snd_sc1810_init_mixer(mixer); + break; } return err; diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c new file mode 100644 index 000000000000..6483e47bafd0 --- /dev/null +++ b/sound/usb/mixer_s1810c.c @@ -0,0 +1,595 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Presonus Studio 1810c driver for ALSA + * Copyright (C) 2019 Nick Kossifidis <mickflemm@gmail.com> + * + * Based on reverse engineering of the communication protocol + * between the windows driver / Univeral Control (UC) program + * and the device, through usbmon. + * + * For now this bypasses the mixer, with all channels split, + * so that the software can mix with greater flexibility. + * It also adds controls for the 4 buttons on the front of + * the device. + */ + +#include <linux/usb.h> +#include <linux/usb/audio-v2.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/control.h> + +#include "usbaudio.h" +#include "mixer.h" +#include "mixer_quirks.h" +#include "helper.h" +#include "mixer_s1810c.h" + +#define SC1810C_CMD_REQ 160 +#define SC1810C_CMD_REQTYPE \ + (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT) +#define SC1810C_CMD_F1 0x50617269 +#define SC1810C_CMD_F2 0x14 + +/* + * DISCLAIMER: These are just guesses based on the + * dumps I got. + * + * It seems like a selects between + * device (0), mixer (0x64) and output (0x65) + * + * For mixer (0x64): + * * b selects an input channel (see below). + * * c selects an output channel pair (see below). + * * d selects left (0) or right (1) of that pair. + * * e 0-> disconnect, 0x01000000-> connect, + * 0x0109-> used for stereo-linking channels, + * e is also used for setting volume levels + * in which case b is also set so I guess + * this way it is possible to set the volume + * level from the specified input to the + * specified output. + * + * IN Channels: + * 0 - 7 Mic/Inst/Line (Analog inputs) + * 8 - 9 S/PDIF + * 10 - 17 ADAT + * 18 - 35 DAW (Inputs from the host) + * + * OUT Channels (pairs): + * 0 -> Main out + * 1 -> Line1/2 + * 2 -> Line3/4 + * 3 -> S/PDIF + * 4 -> ADAT? + * + * For device (0): + * * b and c are not used, at least not on the + * dumps I got. + * * d sets the control id to be modified + * (see below). + * * e sets the setting for that control. + * (so for the switches I was interested + * in it's 0/1) + * + * For output (0x65): + * * b is the output channel (see above). + * * c is zero. + * * e I guess the same as with mixer except 0x0109 + * which I didn't see in my dumps. + * + * The two fixed fields have the same values for + * mixer and output but a different set for device. + */ +struct s1810c_ctl_packet { + u32 a; + u32 b; + u32 fixed1; + u32 fixed2; + u32 c; + u32 d; + u32 e; +}; + +#define SC1810C_CTL_LINE_SW 0 +#define SC1810C_CTL_MUTE_SW 1 +#define SC1810C_CTL_AB_SW 3 +#define SC1810C_CTL_48V_SW 4 + +#define SC1810C_SET_STATE_REQ 161 +#define SC1810C_SET_STATE_REQTYPE SC1810C_CMD_REQTYPE +#define SC1810C_SET_STATE_F1 0x64656D73 +#define SC1810C_SET_STATE_F2 0xF4 + +#define SC1810C_GET_STATE_REQ 162 +#define SC1810C_GET_STATE_REQTYPE \ + (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN) +#define SC1810C_GET_STATE_F1 SC1810C_SET_STATE_F1 +#define SC1810C_GET_STATE_F2 SC1810C_SET_STATE_F2 + +#define SC1810C_STATE_F1_IDX 2 +#define SC1810C_STATE_F2_IDX 3 + +/* + * This packet includes mixer volumes and + * various other fields, it's an extended + * version of ctl_packet, with a and b + * being zero and different f1/f2. + */ +struct s1810c_state_packet { + u32 fields[63]; +}; + +#define SC1810C_STATE_48V_SW 58 +#define SC1810C_STATE_LINE_SW 59 +#define SC1810C_STATE_MUTE_SW 60 +#define SC1810C_STATE_AB_SW 62 + +struct s1810_mixer_state { + uint16_t seqnum; + struct mutex usb_mutex; + struct mutex data_mutex; +}; + +static int +snd_s1810c_send_ctl_packet(struct usb_device *dev, u32 a, + u32 b, u32 c, u32 d, u32 e) +{ + struct s1810c_ctl_packet pkt = { 0 }; + int ret = 0; + + pkt.fixed1 = SC1810C_CMD_F1; + pkt.fixed2 = SC1810C_CMD_F2; + + pkt.a = a; + pkt.b = b; + pkt.c = c; + pkt.d = d; + /* + * Value for settings 0/1 for this + * output channel is always 0 (probably because + * there is no ADAT output on 1810c) + */ + pkt.e = (c == 4) ? 0 : e; + + ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), + SC1810C_CMD_REQ, + SC1810C_CMD_REQTYPE, 0, 0, &pkt, sizeof(pkt)); + if (ret < 0) { + dev_warn(&dev->dev, "could not send ctl packet\n"); + return ret; + } + return 0; +} + +/* + * When opening Universal Control the program periodicaly + * sends and receives state packets for syncinc state between + * the device and the host. + * + * Note that if we send only the request to get data back we'll + * get an error, we need to first send an empty state packet and + * then ask to receive a filled. Their seqnumbers must also match. + */ +static int +snd_sc1810c_get_status_field(struct usb_device *dev, + u32 *field, int field_idx, uint16_t *seqnum) +{ + struct s1810c_state_packet pkt_out = { { 0 } }; + struct s1810c_state_packet pkt_in = { { 0 } }; + int ret = 0; + + pkt_out.fields[SC1810C_STATE_F1_IDX] = SC1810C_SET_STATE_F1; + pkt_out.fields[SC1810C_STATE_F2_IDX] = SC1810C_SET_STATE_F2; + ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), + SC1810C_SET_STATE_REQ, + SC1810C_SET_STATE_REQTYPE, + (*seqnum), 0, &pkt_out, sizeof(pkt_out)); + if (ret < 0) { + dev_warn(&dev->dev, "could not send state packet (%d)\n", ret); + return ret; + } + + ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), + SC1810C_GET_STATE_REQ, + SC1810C_GET_STATE_REQTYPE, + (*seqnum), 0, &pkt_in, sizeof(pkt_in)); + if (ret < 0) { + dev_warn(&dev->dev, "could not get state field %u (%d)\n", + field_idx, ret); + return ret; + } + + (*field) = pkt_in.fields[field_idx]; + (*seqnum)++; + return 0; +} + +/* + * This is what I got when bypassing the mixer with + * all channels split. I'm not 100% sure of what's going + * on, I could probably clean this up based on my observations + * but I prefer to keep the same behavior as the windows driver. + */ +static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) +{ + u32 a, b, c, e, n, off; + struct usb_device *dev = chip->dev; + + /* Set initial volume levels ? */ + a = 0x64; + e = 0xbc; + for (n = 0; n < 2; n++) { + off = n * 18; + for (b = off, c = 0; b < 18 + off; b++) { + /* This channel to all outputs ? */ + for (c = 0; c <= 8; c++) { + snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); + snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e); + } + /* This channel to main output (again) */ + snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e); + snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e); + } + /* + * I noticed on UC that DAW channels have different + * initial volumes, so this makes sense. + */ + e = 0xb53bf0; + } + + /* Connect analog outputs ? */ + a = 0x65; + e = 0x01000000; + for (b = 1; b < 3; b++) { + snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e); + snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e); + } + snd_s1810c_send_ctl_packet(dev, a, 0, 0, 0, e); + snd_s1810c_send_ctl_packet(dev, a, 0, 0, 1, e); + + /* Set initial volume levels for S/PDIF mappings ? */ + a = 0x64; + e = 0xbc; + c = 3; + for (n = 0; n < 2; n++) { + off = n * 18; + for (b = off; b < 18 + off; b++) { + snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); + snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e); + } + e = 0xb53bf0; + } + + /* Connect S/PDIF output ? */ + a = 0x65; + e = 0x01000000; + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e); + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e); + + /* Connect all outputs (again) ? */ + a = 0x65; + e = 0x01000000; + for (b = 0; b < 4; b++) { + snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e); + snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e); + } + + /* Basic routing to get sound out of the device */ + a = 0x64; + e = 0x01000000; + for (c = 0; c < 4; c++) { + for (b = 0; b < 36; b++) { + if ((c == 0 && b == 18) || /* DAW1/2 -> Main */ + (c == 1 && b == 20) || /* DAW3/4 -> Line3/4 */ + (c == 2 && b == 22) || /* DAW4/5 -> Line5/6 */ + (c == 3 && b == 24)) { /* DAW5/6 -> S/PDIF */ + /* Left */ + snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); + snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0); + b++; + /* Right */ + snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0); + snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e); + } else { + /* Leave the rest disconnected */ + snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0); + snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0); + } + } + } + + /* Set initial volume levels for S/PDIF (again) ? */ + a = 0x64; + e = 0xbc; + c = 3; + for (n = 0; n < 2; n++) { + off = n * 18; + for (b = off; b < 18 + off; b++) { + snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); + snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e); + } + e = 0xb53bf0; + } + + /* Connect S/PDIF outputs (again) ? */ + a = 0x65; + e = 0x01000000; + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e); + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e); + + /* Again ? */ + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e); + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e); + + return 0; +} + +/* + * Sync state with the device and retrieve the requested field, + * whose index is specified in (kctl->private_value & 0xFF), + * from the received fields array. + */ +static int +snd_s1810c_get_switch_state(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl, u32 *state) +{ + struct snd_usb_audio *chip = mixer->chip; + struct s1810_mixer_state *private = mixer->private_data; + u32 field = 0; + u32 ctl_idx = (u32) (kctl->private_value & 0xFF); + int ret = 0; + + mutex_lock(&private->usb_mutex); + ret = snd_sc1810c_get_status_field(chip->dev, &field, + ctl_idx, &private->seqnum); + if (ret < 0) + goto unlock; + + *state = field; + unlock: + mutex_unlock(&private->usb_mutex); + return ret ? ret : 0; +} + +/* + * Send a control packet to the device for the control id + * specified in (kctl->private_value >> 8) with value + * specified in (kctl->private_value >> 16). + */ +static int +snd_s1810c_set_switch_state(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl) +{ + struct snd_usb_audio *chip = mixer->chip; + struct s1810_mixer_state *private = mixer->private_data; + u32 pval = (u32) kctl->private_value; + u32 ctl_id = (pval >> 8) & 0xFF; + u32 ctl_val = (pval >> 16) & 0x1; + int ret = 0; + + mutex_lock(&private->usb_mutex); + ret = snd_s1810c_send_ctl_packet(chip->dev, 0, 0, 0, ctl_id, ctl_val); + mutex_unlock(&private->usb_mutex); + return ret; +} + +/* Generic get/set/init functions for switch controls */ + +static int +snd_s1810c_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ctl_elem) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); + struct usb_mixer_interface *mixer = list->mixer; + struct s1810_mixer_state *private = mixer->private_data; + u32 pval = (u32) kctl->private_value; + u32 ctl_idx = pval & 0xFF; + u32 state = 0; + int ret = 0; + + mutex_lock(&private->data_mutex); + ret = snd_s1810c_get_switch_state(mixer, kctl, &state); + if (ret < 0) + goto unlock; + + switch (ctl_idx) { + case SC1810C_STATE_LINE_SW: + case SC1810C_STATE_AB_SW: + ctl_elem->value.enumerated.item[0] = (int)state; + break; + default: + ctl_elem->value.integer.value[0] = (long)state; + } + + unlock: + mutex_unlock(&private->data_mutex); + return (ret < 0) ? ret : 0; +} + +static int +snd_s1810c_switch_set(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ctl_elem) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); + struct usb_mixer_interface *mixer = list->mixer; + struct s1810_mixer_state *private = mixer->private_data; + u32 pval = (u32) kctl->private_value; + u32 ctl_idx = pval & 0xFF; + u32 curval = 0; + u32 newval = 0; + int ret = 0; + + mutex_lock(&private->data_mutex); + ret = snd_s1810c_get_switch_state(mixer, kctl, &curval); + if (ret < 0) + goto unlock; + + switch (ctl_idx) { + case SC1810C_STATE_LINE_SW: + case SC1810C_STATE_AB_SW: + newval = (u32) ctl_elem->value.enumerated.item[0]; + break; + default: + newval = (u32) ctl_elem->value.integer.value[0]; + } + + if (curval == newval) + goto unlock; + + kctl->private_value &= ~(0x1 << 16); + kctl->private_value |= (unsigned int)(newval & 0x1) << 16; + ret = snd_s1810c_set_switch_state(mixer, kctl); + + unlock: + mutex_unlock(&private->data_mutex); + return (ret < 0) ? 0 : 1; +} + +static int +snd_s1810c_switch_init(struct usb_mixer_interface *mixer, + const struct snd_kcontrol_new *new_kctl) +{ + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *elem; + + elem = kzalloc(sizeof(struct usb_mixer_elem_info), GFP_KERNEL); + if (!elem) + return -ENOMEM; + + elem->head.mixer = mixer; + elem->control = 0; + elem->head.id = 0; + elem->channels = 1; + + kctl = snd_ctl_new1(new_kctl, elem); + if (!kctl) { + kfree(elem); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; + + return snd_usb_mixer_add_control(&elem->head, kctl); +} + +static int +snd_s1810c_line_sw_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + static const char *const texts[2] = { + "Preamp On (Mic/Inst)", + "Preamp Off (Line in)" + }; + + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); +} + +static const struct snd_kcontrol_new snd_s1810c_line_sw = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line 1/2 Source Type", + .info = snd_s1810c_line_sw_info, + .get = snd_s1810c_switch_get, + .put = snd_s1810c_switch_set, + .private_value = (SC1810C_STATE_LINE_SW | SC1810C_CTL_LINE_SW << 8) +}; + +static const struct snd_kcontrol_new snd_s1810c_mute_sw = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mute Main Out Switch", + .info = snd_ctl_boolean_mono_info, + .get = snd_s1810c_switch_get, + .put = snd_s1810c_switch_set, + .private_value = (SC1810C_STATE_MUTE_SW | SC1810C_CTL_MUTE_SW << 8) +}; + +static const struct snd_kcontrol_new snd_s1810c_48v_sw = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "48V Phantom Power On Mic Inputs Switch", + .info = snd_ctl_boolean_mono_info, + .get = snd_s1810c_switch_get, + .put = snd_s1810c_switch_set, + .private_value = (SC1810C_STATE_48V_SW | SC1810C_CTL_48V_SW << 8) +}; + +static int +snd_s1810c_ab_sw_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + static const char *const texts[2] = { + "1/2", + "3/4" + }; + + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); +} + +static const struct snd_kcontrol_new snd_s1810c_ab_sw = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone 1 Source Route", + .info = snd_s1810c_ab_sw_info, + .get = snd_s1810c_switch_get, + .put = snd_s1810c_switch_set, + .private_value = (SC1810C_STATE_AB_SW | SC1810C_CTL_AB_SW << 8) +}; + +static void snd_sc1810_mixer_state_free(struct usb_mixer_interface *mixer) +{ + struct s1810_mixer_state *private = mixer->private_data; + kfree(private); + mixer->private_data = NULL; +} + +/* Entry point, called from mixer_quirks.c */ +int snd_sc1810_init_mixer(struct usb_mixer_interface *mixer) +{ + struct s1810_mixer_state *private = NULL; + struct snd_usb_audio *chip = mixer->chip; + struct usb_device *dev = chip->dev; + int ret = 0; + + /* Run this only once */ + if (!list_empty(&chip->mixer_list)) + return 0; + + dev_info(&dev->dev, + "Presonus Studio 1810c, device_setup: %u\n", chip->setup); + if (chip->setup == 1) + dev_info(&dev->dev, "(8out/18in @ 48KHz)\n"); + else if (chip->setup == 2) + dev_info(&dev->dev, "(6out/8in @ 192KHz)\n"); + else + dev_info(&dev->dev, "(8out/14in @ 96KHz)\n"); + + ret = snd_s1810c_init_mixer_maps(chip); + if (ret < 0) + return ret; + + private = kzalloc(sizeof(struct s1810_mixer_state), GFP_KERNEL); + if (!private) + return -ENOMEM; + + mutex_init(&private->usb_mutex); + mutex_init(&private->data_mutex); + + mixer->private_data = private; + mixer->private_free = snd_sc1810_mixer_state_free; + + private->seqnum = 1; + + ret = snd_s1810c_switch_init(mixer, &snd_s1810c_line_sw); + if (ret < 0) + return ret; + + ret = snd_s1810c_switch_init(mixer, &snd_s1810c_mute_sw); + if (ret < 0) + return ret; + + ret = snd_s1810c_switch_init(mixer, &snd_s1810c_48v_sw); + if (ret < 0) + return ret; + + ret = snd_s1810c_switch_init(mixer, &snd_s1810c_ab_sw); + if (ret < 0) + return ret; + return ret; +} diff --git a/sound/usb/mixer_s1810c.h b/sound/usb/mixer_s1810c.h new file mode 100644 index 000000000000..a79a3743cff3 --- /dev/null +++ b/sound/usb/mixer_s1810c.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Presonus Studio 1810c driver for ALSA + * Copyright (C) 2019 Nick Kossifidis <mickflemm@gmail.com> + */ + +int snd_sc1810_init_mixer(struct usb_mixer_interface *mixer); diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index bd258f1ec2dd..a4e4064f9aee 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -357,7 +357,12 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ep = 0x81; ifnum = 1; goto add_sync_ep_from_ifnum; - case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */ + case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II/IIc */ + /* MicroBook IIc */ + if (altsd->bInterfaceClass == USB_CLASS_AUDIO) + return 0; + + /* MicroBook II */ ep = 0x84; ifnum = 0; goto add_sync_ep_from_ifnum; diff --git a/sound/usb/proc.c b/sound/usb/proc.c index ffbf4bd9208c..4174ad11fca6 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -70,7 +70,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s snd_iprintf(buffer, " Interface %d\n", fp->iface); snd_iprintf(buffer, " Altset %d\n", fp->altsetting); snd_iprintf(buffer, " Format:"); - for (fmt = 0; fmt <= SNDRV_PCM_FORMAT_LAST; ++fmt) + pcm_for_each_format(fmt) if (fp->formats & pcm_format_to_bits(fmt)) snd_iprintf(buffer, " %s", snd_pcm_format_name(fmt)); diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index d187aa6d50db..1c8719292eee 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3472,7 +3472,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), }, /* MOTU Microbook II */ { - USB_DEVICE(0x07fd, 0x0004), + USB_DEVICE_VENDOR_SPEC(0x07fd, 0x0004), .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { .vendor_name = "MOTU", .product_name = "MicroBookII", diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 7f558f4b4520..86f192a3043d 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1252,6 +1252,38 @@ static int fasttrackpro_skip_setting_quirk(struct snd_usb_audio *chip, return 0; /* keep this altsetting */ } +static int s1810c_skip_setting_quirk(struct snd_usb_audio *chip, + int iface, int altno) +{ + /* + * Altno settings: + * + * Playback (Interface 1): + * 1: 6 Analog + 2 S/PDIF + * 2: 6 Analog + 2 S/PDIF + * 3: 6 Analog + * + * Capture (Interface 2): + * 1: 8 Analog + 2 S/PDIF + 8 ADAT + * 2: 8 Analog + 2 S/PDIF + 4 ADAT + * 3: 8 Analog + */ + + /* + * I'll leave 2 as the default one and + * use device_setup to switch to the + * other two. + */ + if ((chip->setup == 0 || chip->setup > 2) && altno != 2) + return 1; + else if (chip->setup == 1 && altno != 1) + return 1; + else if (chip->setup == 2 && altno != 3) + return 1; + + return 0; +} + int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, int iface, int altno) @@ -1265,6 +1297,10 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, /* fasttrackpro usb: skip altsets incompatible with device_setup */ 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)) + return s1810c_skip_setting_quirk(chip, iface, altno); + return 0; } @@ -1316,7 +1352,15 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */ return snd_usb_axefx3_boot_quirk(dev); case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */ - return snd_usb_motu_microbookii_boot_quirk(dev); + /* + * For some reason interface 3 with vendor-spec class is + * detected on MicroBook IIc. + */ + if (get_iface_desc(intf->altsetting)->bInterfaceClass == + USB_CLASS_VENDOR_SPEC && + get_iface_desc(intf->altsetting)->bInterfaceNumber < 3) + return snd_usb_motu_microbookii_boot_quirk(dev); + break; } return 0; @@ -1754,5 +1798,47 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, else fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC; break; + case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook IIc */ + /* + * MaxPacketsOnly attribute is erroneously set in endpoint + * descriptors. As a result this card produces noise with + * all sample rates other than 96 KHz. + */ + fp->attributes &= ~UAC_EP_CS_ATTR_FILL_MAX; + break; } } + +/* + * registration quirk: + * the registration is skipped if a device matches with the given ID, + * unless the interface reaches to the defined one. This is for delaying + * the registration until the last known interface, so that the card and + * devices appear at the same time. + */ + +struct registration_quirk { + unsigned int usb_id; /* composed via USB_ID() */ + unsigned int interface; /* the interface to trigger register */ +}; + +#define REG_QUIRK_ENTRY(vendor, product, iface) \ + { .usb_id = USB_ID(vendor, product), .interface = (iface) } + +static const struct registration_quirk registration_quirks[] = { + REG_QUIRK_ENTRY(0x0951, 0x16d8, 2), /* Kingston HyperX AMP */ + { 0 } /* terminator */ +}; + +/* return true if skipping registration */ +bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface) +{ + const struct registration_quirk *q; + + for (q = registration_quirks; q->usb_id; q++) + if (chip->usb_id == q->usb_id) + return iface != q->interface; + + /* Register as normal */ + return false; +} diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index df0355843a4c..c76cf24a640a 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -51,4 +51,6 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, struct audioformat *fp, int stream); +bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface); + #endif /* __USBAUDIO_QUIRKS_H */ diff --git a/sound/usb/stream.c b/sound/usb/stream.c index afd5aa574611..15296f2c902c 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -502,6 +502,9 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, subs = &as->substream[stream]; if (subs->ep_num) continue; + if (snd_device_get_state(chip->card, as->pcm) != + SNDRV_DEV_BUILD) + chip->need_delayed_register = true; err = snd_pcm_new_stream(as->pcm, stream, 1); if (err < 0) return err; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 6fe3ab582ec6..1c892c7f14d7 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -34,6 +34,7 @@ struct snd_usb_audio { unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ unsigned int tx_length_quirk:1; /* Put length specifier in transfers */ unsigned int setup_fmt_after_resume_quirk:1; /* setup the format to interface after resume */ + unsigned int need_delayed_register:1; /* warn for delayed registration */ int num_interfaces; int num_suspended_intf; int sample_rate_read_error; diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 772f6f3ccbb1..37d290fe9d43 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -906,11 +906,12 @@ static const struct snd_pcm_ops snd_usX2Y_pcm_ops = */ static void usX2Y_audio_stream_free(struct snd_usX2Y_substream **usX2Y_substream) { - kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]); - usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL; + int stream; - kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]); - usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL; + for_each_pcm_streams(stream) { + kfree(usX2Y_substream[stream]); + usX2Y_substream[stream] = NULL; + } } static void snd_usX2Y_pcm_private_free(struct snd_pcm *pcm) |