summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,aiu.yaml113
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml51
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml113
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,t9015.yaml58
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs42l51.yaml69
-rw-r--r--Documentation/devicetree/bindings/sound/cs42l51.txt33
-rw-r--r--Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt44
-rw-r--r--Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml62
-rw-r--r--Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt7
-rw-r--r--Documentation/devicetree/bindings/sound/rt5682.txt12
-rw-r--r--Documentation/devicetree/bindings/sound/st,stm32-i2s.txt62
-rw-r--r--Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml87
-rw-r--r--Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt56
-rw-r--r--Documentation/devicetree/bindings/sound/st,stm32-spdifrx.yaml80
-rw-r--r--Documentation/devicetree/bindings/sound/tas2562.txt2
-rw-r--r--Documentation/devicetree/bindings/sound/tlv320adcx140.yaml83
-rw-r--r--Documentation/sound/soc/codec-to-codec.rst9
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi.c54
-rw-r--r--drivers/soundwire/qcom.c7
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--include/dt-bindings/sound/meson-aiu.h18
-rw-r--r--include/dt-bindings/sound/meson-g12a-toacodec.h10
-rw-r--r--include/sound/compress_driver.h40
-rw-r--r--include/sound/hdaudio.h2
-rw-r--r--include/sound/pcm.h14
-rw-r--r--include/sound/rt5682.h8
-rw-r--r--include/sound/soc-dai.h44
-rw-r--r--include/sound/soc-dapm.h6
-rw-r--r--include/sound/soc-dpcm.h16
-rw-r--r--include/sound/soc.h34
-rw-r--r--include/sound/sof/header.h11
-rw-r--r--include/sound/sof/topology.h3
-rw-r--r--include/uapi/sound/sof/abi.h2
-rw-r--r--sound/core/compress_offload.c42
-rw-r--r--sound/core/pcm_dmaengine.c6
-rw-r--r--sound/core/pcm_misc.c18
-rw-r--r--sound/soc/amd/Kconfig10
-rw-r--r--sound/soc/amd/Makefile2
-rw-r--r--sound/soc/amd/acp3x-rt5682-max9836.c376
-rw-r--r--sound/soc/amd/raven/acp3x-i2s.c44
-rw-r--r--sound/soc/amd/raven/pci-acp3x.c7
-rw-r--r--sound/soc/codecs/Kconfig627
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/cros_ec_codec.c25
-rw-r--r--sound/soc/codecs/cs4271.c4
-rw-r--r--sound/soc/codecs/max98357a.c36
-rw-r--r--sound/soc/codecs/mt6660.c79
-rw-r--r--sound/soc/codecs/rk3328_codec.c31
-rw-r--r--sound/soc/codecs/rl6231.c1
-rw-r--r--sound/soc/codecs/rl6231.h2
-rw-r--r--sound/soc/codecs/rt1015.c10
-rw-r--r--sound/soc/codecs/rt5659.c2
-rw-r--r--sound/soc/codecs/rt5682-sdw.c333
-rw-r--r--sound/soc/codecs/rt5682-sdw.h20
-rw-r--r--sound/soc/codecs/rt5682.c1268
-rw-r--r--sound/soc/codecs/rt5682.h98
-rw-r--r--sound/soc/codecs/tas2562.c119
-rw-r--r--sound/soc/codecs/tas2562.h12
-rw-r--r--sound/soc/codecs/tlv320adcx140.c920
-rw-r--r--sound/soc/codecs/tlv320adcx140.h131
-rw-r--r--sound/soc/codecs/wcd934x.c37
-rw-r--r--sound/soc/codecs/wm0010.c2
-rw-r--r--sound/soc/dwc/dwc-i2s.c8
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c4
-rw-r--r--sound/soc/generic/simple-card-utils.c48
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c2
-rw-r--r--sound/soc/intel/atom/sst/sst_pci.c2
-rw-r--r--sound/soc/intel/boards/Kconfig14
-rw-r--r--sound/soc/intel/boards/Makefile2
-rw-r--r--sound/soc/intel/boards/cml_rt1011_rt5682.c3
-rw-r--r--sound/soc/intel/boards/kbl_da7219_max98927.c8
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_max98927.c2
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c2
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_common.h4
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c25
-rw-r--r--sound/soc/intel/boards/sof_pcm512x.c428
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-bxt-match.c2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cht-match.c7
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c10
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c48
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c19
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c113
-rw-r--r--sound/soc/meson/Kconfig41
-rw-r--r--sound/soc/meson/Makefile19
-rw-r--r--sound/soc/meson/aiu-acodec-ctrl.c203
-rw-r--r--sound/soc/meson/aiu-codec-ctrl.c151
-rw-r--r--sound/soc/meson/aiu-encoder-i2s.c365
-rw-r--r--sound/soc/meson/aiu-encoder-spdif.c209
-rw-r--r--sound/soc/meson/aiu-fifo-i2s.c153
-rw-r--r--sound/soc/meson/aiu-fifo-spdif.c186
-rw-r--r--sound/soc/meson/aiu-fifo.c223
-rw-r--r--sound/soc/meson/aiu-fifo.h50
-rw-r--r--sound/soc/meson/aiu.c388
-rw-r--r--sound/soc/meson/aiu.h89
-rw-r--r--sound/soc/meson/axg-card.c406
-rw-r--r--sound/soc/meson/g12a-toacodec.c252
-rw-r--r--sound/soc/meson/g12a-tohdmitx.c219
-rw-r--r--sound/soc/meson/gx-card.c141
-rw-r--r--sound/soc/meson/meson-card-utils.c385
-rw-r--r--sound/soc/meson/meson-card.h55
-rw-r--r--sound/soc/meson/meson-codec-glue.c149
-rw-r--r--sound/soc/meson/meson-codec-glue.h32
-rw-r--r--sound/soc/meson/t9015.c333
-rw-r--r--sound/soc/qcom/apq8016_sbc.c7
-rw-r--r--sound/soc/qcom/lpass-platform.c2
-rw-r--r--sound/soc/qcom/sdm845.c20
-rw-r--r--sound/soc/samsung/Kconfig4
-rw-r--r--sound/soc/samsung/arndale.c4
-rw-r--r--sound/soc/samsung/littlemill.c2
-rw-r--r--sound/soc/samsung/lowland.c2
-rw-r--r--sound/soc/samsung/odroid.c4
-rw-r--r--sound/soc/samsung/smdk_wm8994.c2
-rw-r--r--sound/soc/samsung/smdk_wm8994pcm.c2
-rw-r--r--sound/soc/samsung/snow.c4
-rw-r--r--sound/soc/samsung/speyside.c2
-rw-r--r--sound/soc/samsung/tm2_wm5110.c3
-rw-r--r--sound/soc/samsung/tobermory.c2
-rw-r--r--sound/soc/sh/fsi.c3
-rw-r--r--sound/soc/soc-compress.c5
-rw-r--r--sound/soc/soc-core.c234
-rw-r--r--sound/soc/soc-dai.c18
-rw-r--r--sound/soc/soc-dapm.c211
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c26
-rw-r--r--sound/soc/soc-pcm.c1594
-rw-r--r--sound/soc/soc-topology.c2
-rw-r--r--sound/soc/sof/Kconfig9
-rw-r--r--sound/soc/sof/Makefile1
-rw-r--r--sound/soc/sof/compress.c146
-rw-r--r--sound/soc/sof/compress.h31
-rw-r--r--sound/soc/sof/core.c10
-rw-r--r--sound/soc/sof/debug.c226
-rw-r--r--sound/soc/sof/imx/imx8.c57
-rw-r--r--sound/soc/sof/intel/Kconfig9
-rw-r--r--sound/soc/sof/intel/Makefile1
-rw-r--r--sound/soc/sof/intel/apl.c9
-rw-r--r--sound/soc/sof/intel/cnl.c46
-rw-r--r--sound/soc/sof/intel/hda-codec.c11
-rw-r--r--sound/soc/sof/intel/hda-compress.c114
-rw-r--r--sound/soc/sof/intel/hda-dai.c28
-rw-r--r--sound/soc/sof/intel/hda-dsp.c288
-rw-r--r--sound/soc/sof/intel/hda-ipc.c4
-rw-r--r--sound/soc/sof/intel/hda-loader.c6
-rw-r--r--sound/soc/sof/intel/hda-pcm.c8
-rw-r--r--sound/soc/sof/intel/hda-stream.c25
-rw-r--r--sound/soc/sof/intel/hda.c27
-rw-r--r--sound/soc/sof/intel/hda.h53
-rw-r--r--sound/soc/sof/ipc.c41
-rw-r--r--sound/soc/sof/ops.h59
-rw-r--r--sound/soc/sof/pcm.c15
-rw-r--r--sound/soc/sof/pm.c176
-rw-r--r--sound/soc/sof/probe.c290
-rw-r--r--sound/soc/sof/probe.h85
-rw-r--r--sound/soc/sof/sof-audio.c59
-rw-r--r--sound/soc/sof/sof-audio.h3
-rw-r--r--sound/soc/sof/sof-of-dev.c10
-rw-r--r--sound/soc/sof/sof-priv.h68
-rw-r--r--sound/soc/sprd/Kconfig2
-rw-r--r--sound/soc/sprd/sprd-mcdt.h2
-rw-r--r--sound/soc/stm/stm32_i2s.c39
-rw-r--r--sound/soc/stm/stm32_sai.c26
-rw-r--r--sound/soc/stm/stm32_sai_sub.c11
-rw-r--r--sound/soc/stm/stm32_spdifrx.c29
-rw-r--r--sound/soc/sunxi/sun8i-codec.c3
-rw-r--r--sound/soc/ti/Kconfig8
-rw-r--r--sound/soc/ti/Makefile2
-rw-r--r--sound/soc/ti/davinci-mcasp.c13
-rw-r--r--sound/soc/ti/udma-pcm.c43
-rw-r--r--sound/soc/ti/udma-pcm.h18
-rw-r--r--sound/soc/zte/zx-spdif.c1
-rw-r--r--sound/soc/zte/zx-tdm.c3
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c9
173 files changed, 11869 insertions, 2690 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 = <&amp>;
+ 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/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 = <&reg_audio>;
+ VD-supply = <&reg_audio>;
+ VA-supply = <&reg_audio>;
+ VAHP-supply = <&reg_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 = <&reg_audio>;
- VD-supply = <&reg_audio>;
- VA-supply = <&reg_audio>;
- VAHP-supply = <&reg_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..94a85d0cbf43
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml
@@ -0,0 +1,62 @@
+# 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 {
+ compatible = "shared-dma-pool";
+ reg = <0 0x52800000 0 0x100000>;
+ no-map;
+ };
+ cros-ec@0 {
+ compatible = "google,cros-ec-spi";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ 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/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/rt5682.txt b/Documentation/devicetree/bindings/sound/rt5682.txt
index 30e927a28369..ac98151d29e4 100644
--- a/Documentation/devicetree/bindings/sound/rt5682.txt
+++ b/Documentation/devicetree/bindings/sound/rt5682.txt
@@ -32,6 +32,12 @@ 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".
+
Pins on the device (for linking into audio routes) for RT5682:
* DMIC L1
@@ -53,4 +59,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..1433ff62b14f
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml
@@ -0,0 +1,83 @@
+# 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,use-internal-areg;
+ ti,mic-bias-source = <6>;
+ reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
+ };
+ };
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/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index 5e4a4dbda443..d80017e3d84a 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)
@@ -1651,20 +1668,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;
@@ -1672,9 +1708,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)
@@ -1700,6 +1737,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);
@@ -1708,7 +1746,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/spi/Kconfig b/drivers/spi/Kconfig
index d6ed0c355954..912cd6e35726 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -551,7 +551,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/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/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..f7a95b711100 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,
diff --git a/include/sound/rt5682.h b/include/sound/rt5682.h
index bc2c31734df1..6bf0e3581056 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,8 @@ struct rt5682_platform_data {
enum rt5682_dmic1_clk_pin dmic1_clk_pin;
enum rt5682_jd_src jd_src;
unsigned int btndet_delay;
+
+ const char *dai_clk_names[RT5682_DAI_NUM_CLKS];
};
#endif
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index eaaeb00e9e84..7f70db149b81 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 NULL;
+ */
+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 NULL;
+}
+
#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..40223577ec4a 100644
--- a/include/sound/soc-dpcm.h
+++ b/include/sound/soc-dpcm.h
@@ -132,16 +132,7 @@ 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 *);
#ifdef CONFIG_DEBUG_FS
@@ -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..03054bf9cd37 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
*
@@ -1109,6 +1117,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;
@@ -1132,6 +1148,9 @@ struct snd_soc_pcm_runtime {
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
@@ -1152,13 +1171,20 @@ struct snd_soc_pcm_runtime {
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_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_dai_rollback(rtd, i, dai) \
+#define for_each_rtd_codec_dais_rollback(rtd, i, dai) \
for (; (--(i) >= 0) && ((dai) = rtd->codec_dais[i]);)
+
void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd);
/* mixer control */
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/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/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/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/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index 5749a8a49784..9d4f48cfe47f 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);
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index a6a541511534..5dd2e5335900 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -474,32 +474,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/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/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c
new file mode 100644
index 000000000000..8f71c3f7ef79
--- /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 = rtd->codec_dai;
+ 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 = rtd->codec_dai;
+ 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 = rtd->codec_dai;
+ 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/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/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/max98357a.c b/sound/soc/codecs/max98357a.c
index 16313b973eaa..74f20114297c 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)
diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c
index a36c416caad4..bcec82aa57fb 100644
--- a/sound/soc/codecs/mt6660.c
+++ b/sound/soc/codecs/mt6660.c
@@ -4,12 +4,10 @@
#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/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..e1df2d076533 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);
}
@@ -1232,6 +1252,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 +1301,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)
{
@@ -1516,7 +1554,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 +1630,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 +1727,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 +1745,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 +1826,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 +1914,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 +1924,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 +1935,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 +2105,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 +2281,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;
+
+ return 0;
+}
- 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);
+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->pll_in = freq_in;
- rt5682->pll_out = freq_out;
- rt5682->pll_src = source;
+ 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 +2450,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 +2489,393 @@ 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;
+#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;
+ }
+
+ /* 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
+
+ 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;
+ }
+ }
+
return 0;
}
@@ -2332,7 +2883,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 +2929,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 +3158,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 = {
@@ -2465,6 +3235,13 @@ static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev)
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 +3251,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 +3297,219 @@ 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);
+
+ /* 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 +3576,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);
@@ -2676,7 +3666,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..43de6e802309 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
@@ -738,7 +744,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 +826,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 +847,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 +1061,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 +1125,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 +1349,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 +1377,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..6b7f7a18da36 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 =
@@ -411,12 +495,22 @@ static const struct snd_kcontrol_new vsense_switch =
static const struct snd_kcontrol_new tas2562_snd_controls[] = {
SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 0, 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/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/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/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index ece130f59d15..44e5924be870 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -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/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 9b794775df53..abbdf1054f6f 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -331,6 +331,50 @@ 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);
@@ -347,6 +391,10 @@ int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
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/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/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..755e1de19df9 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -457,6 +457,20 @@ config SND_SOC_INTEL_SOF_RT5682_MACH
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))
+ select SND_SOC_PCM512x_I2C
+ select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC
+ 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)
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index b74ddd49bd39..781e7ec58e47 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
@@ -37,6 +38,7 @@ 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
diff --git a/sound/soc/intel/boards/cml_rt1011_rt5682.c b/sound/soc/intel/boards/cml_rt1011_rt5682.c
index dd80d0186a6c..2a6e5b124099 100644
--- a/sound/soc/intel/boards/cml_rt1011_rt5682.c
+++ b/sound/soc/intel/boards/cml_rt1011_rt5682.c
@@ -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,
diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c
index 7a13e9b35187..0ceb1748a262 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 =
diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c
index d8f2ff7139a9..f65feee1c166 100644
--- a/sound/soc/intel/boards/kbl_rt5663_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c
@@ -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
diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
index 96c814f36458..341bb47311a6 100644
--- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
@@ -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) {
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..fe2d3a23a4ef 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;
diff --git a/sound/soc/intel/boards/sof_pcm512x.c b/sound/soc/intel/boards/sof_pcm512x.c
new file mode 100644
index 000000000000..626153bd71e7
--- /dev/null
+++ b/sound/soc/intel/boards/sof_pcm512x.c
@@ -0,0 +1,428 @@
+// 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))
+
+/* 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;
+};
+
+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 = rtd->codec_dai;
+ 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 = rtd->codec_dai->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 = rtd->codec_dai->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 = rtd->codec_dai->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;
+
+ 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)
+{
+ 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;
+
+ idisp_components[i - 1].name = "ehdaudio0D2";
+ idisp_components[i - 1].dai_name = devm_kasprintf(dev,
+ GFP_KERNEL,
+ "intel-hdmi-hifi%d",
+ i);
+ if (!idisp_components[i - 1].dai_name)
+ goto devm_err;
+
+ links[id].codecs = &idisp_components[i - 1];
+ links[id].num_codecs = 1;
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].init = sof_hdmi_init;
+ links[id].dpcm_playback = 1;
+ links[id].no_pcm = 1;
+ id++;
+ }
+
+ return links;
+devm_err:
+ return NULL;
+}
+
+static int sof_audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_links;
+ struct snd_soc_acpi_mach *mach;
+ struct sof_card_private *ctx;
+ int dmic_be_num, hdmi_num;
+ int ret, ssp_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)
+ 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);
+ 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;
+ mach = (&pdev->dev)->platform_data;
+
+ /* 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/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/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index b99509675d29..05a9677c5a53 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:
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 9d5405881209..d1512d483cda 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -93,6 +93,11 @@ enum jz47xx_i2s_version {
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 +109,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 +289,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_JZ4780) {
div_reg &= ~I2SDIV_IDV_MASK;
div_reg |= (div - 1) << I2SDIV_IDV_SHIFT;
} else {
@@ -398,7 +403,7 @@ 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) {
+ if (i2s->soc_info->version >= JZ_I2S_JZ4780) {
conf = (7 << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
(8 << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
@@ -457,6 +462,11 @@ static struct snd_soc_dai_driver jz4740_i2s_dai = {
.ops = &jz4740_i2s_dai_ops,
};
+static const struct i2s_soc_info jz4740_i2s_soc_info = {
+ .version = JZ_I2S_JZ4740,
+ .dai = &jz4740_i2s_dai,
+};
+
static struct snd_soc_dai_driver jz4780_i2s_dai = {
.probe = jz4740_i2s_dai_probe,
.remove = jz4740_i2s_dai_remove,
@@ -475,6 +485,11 @@ static struct snd_soc_dai_driver jz4780_i2s_dai = {
.ops = &jz4740_i2s_dai_ops,
};
+static const struct i2s_soc_info jz4780_i2s_soc_info = {
+ .version = JZ_I2S_JZ4780,
+ .dai = &jz4780_i2s_dai,
+};
+
static const struct snd_soc_component_driver jz4740_i2s_component = {
.name = "jz4740-i2s",
.suspend = jz4740_i2s_suspend,
@@ -483,8 +498,8 @@ 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,jz4780-i2s", .data = &jz4780_i2s_soc_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, jz4740_of_matches);
@@ -492,45 +507,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/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 2e1e61d8f127..5d82159f4f2e 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);
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index ebcc0b86286b..f65e3ebe38b8 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);
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index ef6f23675286..bbc4ad749892 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,7 +99,7 @@ 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)
{
@@ -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(rtd->codec_dai->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-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
index c65493721e90..c4e4f1f99dde 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)
@@ -38,6 +40,7 @@ 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;
@@ -49,8 +52,7 @@ static int mt8183_da7219_i2s_hw_params(struct snd_pcm_substream *substream,
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/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..da8c098e8750
--- /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 rtd->cpu_dai;
+}
+
+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..77a7d5f36ebf 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,
@@ -234,7 +84,7 @@ 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;
@@ -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/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..b5d3c9f56bac
--- /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(rtd->cpu_dai, 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/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index ac75838bbfab..7647af3e51f6 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -34,8 +34,8 @@ 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 *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/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index b05091c283b7..5d1bc5757169 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -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/sdm845.c b/sound/soc/qcom/sdm845.c
index 3b5547a27aad..3ac02204a706 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -43,14 +43,14 @@ 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 *codec_dai;
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, 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) {
+ ret = snd_soc_dai_get_channel_map(codec_dai,
&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
if (ret != 0 && ret != -ENOTSUPP) {
@@ -77,6 +77,7 @@ static int sdm845_tdm_snd_hw_params(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;
int ret = 0, j;
int channels, slot_width;
@@ -125,8 +126,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(
@@ -214,7 +214,6 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
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 +265,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 +274,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);
}
@@ -345,8 +344,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")) {
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..6e6d67d6e0ab 100644
--- a/sound/soc/samsung/arndale.c
+++ b/sound/soc/samsung/arndale.c
@@ -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/littlemill.c b/sound/soc/samsung/littlemill.c
index 59904f44118b..2f2f83a8c23a 100644
--- a/sound/soc/samsung/littlemill.c
+++ b/sound/soc/samsung/littlemill.c
@@ -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..fcc7897ee7d0 100644
--- a/sound/soc/samsung/lowland.c
+++ b/sound/soc/samsung/lowland.c
@@ -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/odroid.c b/sound/soc/samsung/odroid.c
index f0f5fa9c27d3..30c7e1bc2a30 100644
--- a/sound/soc/samsung/odroid.c
+++ b/sound/soc/samsung/odroid.c
@@ -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/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c
index 28f8be000aa1..8fa5f6b387ad 100644
--- a/sound/soc/samsung/smdk_wm8994.c
+++ b/sound/soc/samsung/smdk_wm8994.c
@@ -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..6e44f7927852 100644
--- a/sound/soc/samsung/smdk_wm8994pcm.c
+++ b/sound/soc/samsung/smdk_wm8994pcm.c
@@ -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..bebcf0a4d608 100644
--- a/sound/soc/samsung/snow.c
+++ b/sound/soc/samsung/snow.c
@@ -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/speyside.c b/sound/soc/samsung/speyside.c
index ea0d1ec67f01..8f175f204eb7 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -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..043a287728b3 100644
--- a/sound/soc/samsung/tm2_wm5110.c
+++ b/sound/soc/samsung/tm2_wm5110.c
@@ -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..1aa3fdb4b152 100644
--- a/sound/soc/samsung/tobermory.c
+++ b/sound/soc/samsung/tobermory.c
@@ -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/fsi.c b/sound/soc/sh/fsi.c
index 4b35ef402604..5ef4221be6c3 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -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/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..4e0f55555e37 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,10 +467,10 @@ 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);
@@ -482,6 +484,14 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime(
goto free_rtd;
/*
+ * for rtd->cpu_dais
+ */
+ rtd->cpu_dais = devm_kcalloc(dev, dai_link->num_cpus,
+ sizeof(struct snd_soc_dai *),
+ GFP_KERNEL);
+ if (!rtd->cpu_dais)
+ goto free_rtd;
+ /*
* 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;
@@ -1118,7 +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);
+ ret = soc_dai_pcm_new(rtd->cpu_dais,
+ rtd->num_cpus, rtd);
if (ret < 0)
return ret;
ret = soc_dai_pcm_new(rtd->codec_dais,
@@ -1310,23 +1314,25 @@ static void soc_remove_link_dais(struct snd_soc_card *card)
{
int i;
struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_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)
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
soc_remove_dai(codec_dai, order);
- soc_remove_dai(rtd->cpu_dai, order);
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ soc_remove_dai(cpu_dai, order);
}
}
}
static int soc_probe_link_dais(struct snd_soc_card *card)
{
- struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *codec_dai, *cpu_dai;
struct snd_soc_pcm_runtime *rtd;
int i, order, ret;
@@ -1337,12 +1343,15 @@ 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 CPU DAI */
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ ret = soc_probe_dai(cpu_dai, order);
+ if (ret)
+ return ret;
+ }
/* probe the CODEC DAI */
- for_each_rtd_codec_dai(rtd, i, codec_dai) {
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = soc_probe_dai(codec_dai, order);
if (ret)
return ret;
@@ -1471,12 +1480,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 +1499,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;
@@ -3102,6 +3112,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..e00a465a7c32 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;
@@ -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;
@@ -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,65 +4297,83 @@ 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,
@@ -4359,10 +4382,7 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
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");
@@ -4414,10 +4434,12 @@ 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 *cpu_dai;
int i;
- soc_dapm_dai_stream_event(rtd->cpu_dai, stream, event);
- for_each_rtd_codec_dai(rtd, i, codec_dai)
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ soc_dapm_dai_stream_event(cpu_dai, stream, event);
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
soc_dapm_dai_stream_event(codec_dai, stream, event);
dapm_power_widgets(rtd->card, event);
@@ -4754,6 +4776,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 +4790,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 +4823,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..733d7e8a0e55 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,31 @@ 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 *cpu_dai;
+ struct snd_soc_dai *codec_dai;
+ int i;
+
+ lockdep_assert_held(&rtd->card->pcm_mutex);
+
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ cpu_dai->stream_active[stream] += action;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
+ codec_dai->stream_active[stream] += action;
+
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ cpu_dai->active += action;
+ cpu_dai->component->active += action;
+ }
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ codec_dai->active += action;
+ codec_dai->component->active += action;
+ }
+}
+
/**
* snd_soc_runtime_activate() - Increment active count for PCM runtime components
* @rtd: ASoC PCM runtime that is activated
@@ -94,28 +293,7 @@ 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);
}
/**
@@ -130,28 +308,7 @@ 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);
}
/**
@@ -287,7 +444,7 @@ 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 *cpu_dai;
struct snd_soc_dai *codec_dai;
unsigned int rate, channels, sample_bits, symmetry, i;
@@ -296,40 +453,60 @@ 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_cpu_dais(rtd, i, cpu_dai)
+ symmetry |= cpu_dai->driver->symmetric_rates;
- for_each_rtd_codec_dai(rtd, i, codec_dai)
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
symmetry |= codec_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)
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ symmetry |= cpu_dai->driver->symmetric_channels;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
symmetry |= codec_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_cpu_dais(rtd, i, cpu_dai)
+ symmetry |= cpu_dai->driver->symmetric_samplebits;
- for_each_rtd_codec_dai(rtd, i, codec_dai)
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
symmetry |= codec_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,16 +515,22 @@ 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 *cpu_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_cpu_dais(rtd, i, cpu_dai)
+ symmetry = symmetry ||
+ cpu_dai->driver->symmetric_rates ||
+ cpu_dai->driver->symmetric_channels ||
+ cpu_dai->driver->symmetric_samplebits;
- for_each_rtd_codec_dai(rtd, i, codec_dai)
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
symmetry = symmetry ||
codec_dai->driver->symmetric_rates ||
codec_dai->driver->symmetric_channels ||
@@ -373,77 +556,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) {
+
+ /*
+ * 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);
+ }
- /* first calculate min/max only for CODECs in the DAI link */
- for_each_rtd_codec_dai(rtd, i, codec_dai) {
+ /* 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 +656,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 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_component **last)
+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 +764,49 @@ 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 *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);
+
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ snd_soc_dai_shutdown(cpu_dai, substream);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
+ snd_soc_dai_shutdown(codec_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 +816,10 @@ 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 *cpu_dai;
struct snd_soc_dai *codec_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,25 +830,34 @@ 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);
+ ret = soc_pcm_components_open(substream);
+ if (ret < 0)
+ goto component_err;
+
+ ret = soc_rtd_startup(rtd, substream);
if (ret < 0) {
- dev_err(cpu_dai->dev, "ASoC: can't open interface %s: %d\n",
- cpu_dai->name, ret);
- goto out;
+ pr_err("ASoC: %s startup failed: %d\n",
+ rtd->dai_link->name, ret);
+ goto rtd_startup_err;
}
- ret = soc_pcm_components_open(substream, &component);
- if (ret < 0)
- goto component_err;
+ /* startup the audio subsystem */
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ 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 cpu_dai_err;
+ }
+ }
- for_each_rtd_codec_dai(rtd, i, codec_dai) {
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = snd_soc_dai_startup(codec_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;
+ goto config_err;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -576,13 +866,6 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
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;
- }
-
/* Dynamic PCM DAI links compat checks use dynamic capabilities */
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
goto dynamic;
@@ -593,37 +876,42 @@ 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_cpu_dais(rtd, i, cpu_dai) {
+ 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) {
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (codec_dai->active) {
ret = soc_pcm_apply_symmetry(substream, codec_dai);
if (ret != 0)
@@ -632,7 +920,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
}
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 +935,16 @@ 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)
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
snd_soc_dai_shutdown(codec_dai, substream);
+cpu_dai_err:
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ snd_soc_dai_shutdown(cpu_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 +970,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,7 +978,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 *cpu_dai;
struct snd_soc_dai *codec_dai;
int i, ret = 0;
@@ -769,7 +1000,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
}
}
- for_each_rtd_codec_dai(rtd, i, codec_dai) {
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = snd_soc_dai_prepare(codec_dai, substream);
if (ret < 0) {
dev_err(codec_dai->dev,
@@ -779,11 +1010,13 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
}
}
- 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;
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ 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 */
@@ -796,10 +1029,11 @@ 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)
+ for_each_rtd_codec_dais(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_cpu_dais(rtd, i, cpu_dai)
+ snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
out:
mutex_unlock(&rtd->card->pcm_mutex);
@@ -822,13 +1056,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 +1080,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 +1097,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 +1144,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;
+
+ ret = snd_soc_dai_hw_params(cpu_dai, substream, params);
+ if (ret < 0)
+ goto interface_err;
- /* 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));
+ /* 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 +1183,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,21 +1217,22 @@ 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 *cpu_dai;
struct snd_soc_dai *codec_dai;
- bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
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_cpu_dais(rtd, i, cpu_dai) {
+ if (cpu_dai->active == 1) {
+ cpu_dai->rate = 0;
+ cpu_dai->channels = 0;
+ cpu_dai->sample_bits = 0;
+ }
}
- for_each_rtd_codec_dai(rtd, i, codec_dai) {
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (codec_dai->active == 1) {
codec_dai->rate = 0;
codec_dai->channels = 0;
@@ -988,13 +1241,22 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
}
/* 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))
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ int active = codec_dai->stream_active[substream->stream];
+
+ if (active == 1)
snd_soc_dai_digital_mute(codec_dai, 1,
substream->stream);
}
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ int active = cpu_dai->stream_active[substream->stream];
+
+ if (active == 1)
+ snd_soc_dai_digital_mute(cpu_dai, 1,
+ substream->stream);
+ }
+
/* free any machine hw params */
soc_rtd_hw_free(rtd, substream);
@@ -1002,14 +1264,19 @@ 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) {
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
continue;
snd_soc_dai_hw_free(codec_dai, substream);
}
- snd_soc_dai_hw_free(cpu_dai, substream);
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream))
+ continue;
+
+ snd_soc_dai_hw_free(cpu_dai, substream);
+ }
mutex_unlock(&rtd->card->pcm_mutex);
return 0;
@@ -1019,7 +1286,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 *cpu_dai;
struct snd_soc_dai *codec_dai;
int i, ret;
@@ -1033,11 +1300,13 @@ 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_cpu_dais(rtd, i, cpu_dai) {
+ ret = snd_soc_dai_trigger(cpu_dai, substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
- for_each_rtd_codec_dai(rtd, i, codec_dai) {
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = snd_soc_dai_trigger(codec_dai, substream, cmd);
if (ret < 0)
return ret;
@@ -1050,19 +1319,21 @@ 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 *cpu_dai;
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) {
ret = snd_soc_dai_trigger(codec_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_cpu_dais(rtd, i, cpu_dai) {
+ 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);
@@ -1103,19 +1374,21 @@ 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 *cpu_dai;
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) {
ret = snd_soc_dai_bespoke_trigger(codec_dai, substream, cmd);
if (ret < 0)
return ret;
}
- ret = snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd);
- if (ret < 0)
- return ret;
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ ret = snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
@@ -1127,12 +1400,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 +1417,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 +1440,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 +1464,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 +1518,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 +1533,48 @@ 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) {
+ for_each_card_rtds(card, be) {
- if (!be->dai_link->no_pcm)
- continue;
+ if (!be->dai_link->no_pcm)
+ continue;
+
+ for_each_rtd_cpu_dais(be, i, dai) {
+ w = snd_soc_dai_get_widget(dai, stream);
dev_dbg(card->dev, "ASoC: try BE : %s\n",
- be->cpu_dai->playback_widget ?
- be->cpu_dai->playback_widget->name : "(not set)");
+ w ? w->name : "(not set)");
- if (be->cpu_dai->playback_widget == widget)
+ if (w == 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;
-
- dev_dbg(card->dev, "ASoC: try BE %s\n",
- be->cpu_dai->capture_widget ?
- be->cpu_dai->capture_widget->name : "(not set)");
+ for_each_rtd_codec_dais(be, i, dai) {
+ w = snd_soc_dai_get_widget(dai, stream);
- 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 +1584,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;
-
- 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;
+ int stream;
- 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 +1605,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,6 +1621,11 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
return paths;
}
+void dpcm_path_put(struct snd_soc_dapm_widget_list **list)
+{
+ snd_soc_dapm_dai_free_widgets(list);
+}
+
static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
struct snd_soc_dapm_widget_list **list_)
{
@@ -1410,16 +1641,24 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
unsigned int i;
/* is there a valid CPU DAI widget for this BE */
- widget = dai_get_widget(dpcm->be->cpu_dai, stream);
+ do_prune = 1;
+ for_each_rtd_cpu_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 */
- if (widget && widget_in_list(list, widget))
+ /*
+ * The BE is pruned only if none of the cpu_dai
+ * widgets are in the active list.
+ */
+ if (widget && widget_in_list(list, widget))
+ do_prune = 0;
+ }
+ if (!do_prune)
continue;
/* 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);
+ for_each_rtd_codec_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 */
if (widget && widget_in_list(list, widget))
@@ -1446,12 +1685,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 +1705,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 +1720,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 +1907,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 +1918,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 +1943,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,23 +1998,29 @@ 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_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);
- *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);
+ *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_codec_dais(be, i, dai) {
/*
* Skip CODECs which don't support the current stream
* type. See soc_pcm_init_runtime_hw() for more details
@@ -1788,11 +2028,7 @@ static void dpcm_runtime_merge_rate(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);
*rate_min = max(*rate_min, codec_stream->rate_min);
*rate_max = min_not_zero(*rate_max,
@@ -1807,13 +2043,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 +2094,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 */
@@ -1871,6 +2118,7 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
snd_soc_dpcm_get_substream(be, stream);
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai;
int i;
/* A backend may not have the requested substream */
@@ -1885,14 +2133,16 @@ 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_cpu_dais(rtd, i, cpu_dai) {
+ if (cpu_dai->active) {
+ err = soc_pcm_apply_symmetry(fe_substream,
+ cpu_dai);
+ if (err < 0)
+ return err;
+ }
}
- for_each_rtd_codec_dai(rtd, i, codec_dai) {
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (codec_dai->active) {
err = soc_pcm_apply_symmetry(fe_substream,
codec_dai);
@@ -1913,7 +2163,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 +2184,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 +2244,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 +2422,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 +2746,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 +2898,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,67 +2922,46 @@ 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;
}
@@ -2785,38 +2992,39 @@ out:
mutex_unlock(&card->mutex);
return ret;
}
-int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
+
+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 +3034,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 +3044,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 +3058,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 +3070,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 +3206,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 +3239,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 +3258,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 +3295,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..33909afd3bbc 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -2774,7 +2774,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..3bc64dee7c39 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
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..05125cb0be6e 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -171,23 +171,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 +284,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-dai.c b/sound/soc/sof/intel/hda-dai.c
index 9c6e3f990ee3..ed5e7d2c0d43 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -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
/*
@@ -460,5 +473,20 @@ struct snd_soc_dai_driver skl_dai[] = {
.name = "Alt Analog CPU DAI",
.ops = &hda_link_dai_ops,
},
+#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..79ce52c32ef1 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,179 @@ 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;
}
+/*
+ * 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;
+ dev_dbg(sdev->dev, "New DSP state %d substate %d\n",
+ target_state->state, target_state->substate);
+ 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;
@@ -486,10 +651,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 +682,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 +719,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 +780,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)
@@ -606,3 +824,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..922052883b0a 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;
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 8852184a2569..03b05d7f66da 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)));
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..7daa913dbde0 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -571,6 +571,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 +604,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..7ca887041a34 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -288,10 +288,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);
@@ -365,9 +363,6 @@ 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;
}
@@ -379,7 +374,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
@@ -596,6 +591,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
/* 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,6 +618,9 @@ 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);
@@ -694,12 +694,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 +708,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 = "";
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 6191d9192fae..537c0a930a15 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -11,6 +11,7 @@
#ifndef __SOF_INTEL_HDA_H
#define __SOF_INTEL_HDA_H
+#include <sound/compress_driver.h>
#include <sound/hda_codec.h>
#include <sound/hdaudio_ext.h>
#include "shim.h"
@@ -348,7 +349,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 +399,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 +434,9 @@ struct sof_intel_hda_dev {
/* DMIC device */
struct platform_device *dmic_dev;
+
+ /* delayed work to enter D0I3 opportunistically */
+ struct delayed_work d0i3_work;
};
static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s)
@@ -469,9 +492,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 +504,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 +559,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.
*/
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/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..f4769e19965a 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,
@@ -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
@@ -598,8 +601,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 +623,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 +789,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..eacd10e4da11 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -202,7 +202,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..5d16f668d16a 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;
@@ -387,6 +426,10 @@ struct snd_sof_dev {
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;
struct snd_dma_buffer dmatp;
@@ -417,8 +460,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 +495,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/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/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c
index 3e7226a53e53..2478405727c3 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,16 @@ 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;
}
@@ -903,7 +915,9 @@ 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);
}
@@ -914,8 +928,11 @@ static int stm32_i2s_probe(struct platform_device *pdev)
ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
&stm32_i2s_pcm_config, 0);
- if (ret)
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "PCM DMA register error %d\n", ret);
return ret;
+ }
/* Set SPI/I2S in i2s mode */
ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
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..fe4903260d4e 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -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..49766afdae61 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);
}
@@ -967,7 +971,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,12 +984,16 @@ 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,
@@ -999,7 +1009,8 @@ static int stm32_spdifrx_probe(struct platform_device *pdev)
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);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "PCM DMA register error %d\n", ret);
goto error;
}
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/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/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/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/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/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)