summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2019-11-22 19:56:02 +0000
committerMark Brown <broonie@kernel.org>2019-11-22 19:56:02 +0000
commit8c4d2a0bfbd27d030e4652b714cd5a1598f3559b (patch)
treed8ea72d906a690109d2f7aeeb2dc9ec529c0d5a6
parent3701d2cb87671c227f1aa59226c9a01e9f01c2ea (diff)
parent39870b0dec68ed7dd814beb697e541670975c7d8 (diff)
Merge branch 'asoc-5.5' into asoc-next
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau7118.yaml85
-rw-r--r--Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml267
-rw-r--r--Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml38
-rw-r--r--Documentation/devicetree/bindings/sound/arndale.txt5
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,mqs.txt36
-rw-r--r--Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt24
-rw-r--r--Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt6
-rw-r--r--Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt7
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,fsi.txt31
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,fsi.yaml76
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.txt1
-rw-r--r--Documentation/devicetree/bindings/sound/rockchip-max98090.txt27
-rw-r--r--Documentation/devicetree/bindings/sound/rt1011.txt10
-rw-r--r--Documentation/devicetree/bindings/sound/rt5682.txt6
-rw-r--r--Documentation/devicetree/bindings/sound/samsung,odroid.txt54
-rw-r--r--Documentation/devicetree/bindings/sound/samsung,odroid.yaml91
-rw-r--r--Documentation/devicetree/bindings/sound/samsung-i2s.txt84
-rw-r--r--Documentation/devicetree/bindings/sound/samsung-i2s.yaml138
-rw-r--r--Documentation/devicetree/bindings/sound/sun4i-codec.txt94
-rw-r--r--Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt17
-rw-r--r--Documentation/devicetree/bindings/sound/tas2562.txt34
-rw-r--r--Documentation/devicetree/bindings/sound/tas2770.txt37
-rw-r--r--Documentation/devicetree/bindings/sound/ti,pcm3168a.txt8
-rw-r--r--Documentation/devicetree/bindings/sound/tlv320aic31xx.txt5
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.yaml2
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c11
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c41
-rw-r--r--drivers/platform/chrome/cros_ec_trace.c5
-rw-r--r--include/drm/bridge/dw_hdmi.h4
-rw-r--r--include/dt-bindings/sound/samsung-i2s.h12
-rw-r--r--include/linux/platform_data/cros_ec_commands.h285
-rw-r--r--include/sound/dmaengine_pcm.h5
-rw-r--r--include/sound/hda_codec.h1
-rw-r--r--include/sound/pxa2xx-lib.h26
-rw-r--r--include/sound/rt5682.h1
-rw-r--r--include/sound/simple_card_utils.h1
-rw-r--r--include/sound/soc-acpi-intel-match.h3
-rw-r--r--include/sound/soc-acpi.h4
-rw-r--r--include/sound/soc-component.h49
-rw-r--r--include/sound/soc-dpcm.h18
-rw-r--r--include/sound/soc.h38
-rw-r--r--include/sound/sof.h3
-rw-r--r--include/sound/sof/dai-imx.h34
-rw-r--r--include/sound/sof/dai.h2
-rw-r--r--include/sound/sof/header.h2
-rw-r--r--include/sound/sof/pm.h8
-rw-r--r--include/sound/sof/stream.h4
-rw-r--r--include/sound/wm8904.h2
-rw-r--r--include/uapi/sound/compress_params.h10
-rw-r--r--include/uapi/sound/sof/abi.h2
-rw-r--r--include/uapi/sound/sof/tokens.h11
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c80
-rw-r--r--sound/core/pcm_dmaengine.c83
-rw-r--r--sound/pci/hda/patch_hdmi.c19
-rw-r--r--sound/soc/amd/acp-pcm-dma.c63
-rw-r--r--sound/soc/amd/raven/acp3x-pcm-dma.c56
-rw-r--r--sound/soc/atmel/atmel-pcm-pdc.c48
-rw-r--r--sound/soc/au1x/dbdma2.c64
-rw-r--r--sound/soc/au1x/dma.c63
-rw-r--r--sound/soc/bcm/cygnus-pcm.c56
-rw-r--r--sound/soc/codecs/Kconfig44
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/adau1761.c129
-rw-r--r--sound/soc/codecs/adau7118-hw.c43
-rw-r--r--sound/soc/codecs/adau7118-i2c.c82
-rw-r--r--sound/soc/codecs/adau7118.c586
-rw-r--r--sound/soc/codecs/adau7118.h24
-rw-r--r--sound/soc/codecs/cros_ec_codec.c1128
-rw-r--r--sound/soc/codecs/cx2072x.c2
-rw-r--r--sound/soc/codecs/hdac_hda.c114
-rw-r--r--sound/soc/codecs/hdac_hda.h13
-rw-r--r--sound/soc/codecs/madera.h2
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c54
-rw-r--r--sound/soc/codecs/mt6358.c105
-rw-r--r--sound/soc/codecs/pcm3168a.c143
-rw-r--r--sound/soc/codecs/rt1011.c249
-rw-r--r--sound/soc/codecs/rt1011.h24
-rw-r--r--sound/soc/codecs/rt5514-spi.c41
-rw-r--r--sound/soc/codecs/rt5645.c13
-rw-r--r--sound/soc/codecs/rt5663.c4
-rw-r--r--sound/soc/codecs/rt5677-spi.c399
-rw-r--r--sound/soc/codecs/rt5677-spi.h1
-rw-r--r--sound/soc/codecs/rt5677.c445
-rw-r--r--sound/soc/codecs/rt5677.h11
-rw-r--r--sound/soc/codecs/rt5682.c15
-rw-r--r--sound/soc/codecs/tas2562.c590
-rw-r--r--sound/soc/codecs/tas2562.h85
-rw-r--r--sound/soc/codecs/tas2770.c819
-rw-r--r--sound/soc/codecs/tas2770.h143
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c45
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h8
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c3
-rw-r--r--sound/soc/codecs/wcd9335.c10
-rw-r--r--sound/soc/codecs/wm2200.c5
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm8904.c72
-rw-r--r--sound/soc/codecs/wm8904.h1
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c22
-rw-r--r--sound/soc/codecs/wm8994.c156
-rw-r--r--sound/soc/codecs/wm8994.h10
-rw-r--r--sound/soc/codecs/wm_adsp.c81
-rw-r--r--sound/soc/codecs/wm_adsp.h4
-rw-r--r--sound/soc/dwc/dwc-pcm.c48
-rw-r--r--sound/soc/fsl/Kconfig10
-rw-r--r--sound/soc/fsl/Makefile2
-rw-r--r--sound/soc/fsl/fsl_asrc.c110
-rw-r--r--sound/soc/fsl/fsl_asrc.h7
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c120
-rw-r--r--sound/soc/fsl/fsl_audmix.c6
-rw-r--r--sound/soc/fsl/fsl_audmix.h1
-rw-r--r--sound/soc/fsl/fsl_dma.c54
-rw-r--r--sound/soc/fsl/fsl_esai.c12
-rw-r--r--sound/soc/fsl/fsl_mqs.c335
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c56
-rw-r--r--sound/soc/fsl/mpc5200_dma.c51
-rw-r--r--sound/soc/generic/audio-graph-card.c4
-rw-r--r--sound/soc/generic/simple-card.c4
-rw-r--r--sound/soc/intel/Kconfig17
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c30
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-pcm.c52
-rw-r--r--sound/soc/intel/boards/Kconfig52
-rw-r--r--sound/soc/intel/boards/Makefile14
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c51
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c11
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c11
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c26
-rw-r--r--sound/soc/intel/boards/cml_rt1011_rt5682.c487
-rw-r--r--sound/soc/intel/boards/glk_rt5682_max98357a.c11
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.c85
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.h32
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c149
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_common.c6
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_common.h27
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c3
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c13
-rw-r--r--sound/soc/intel/common/Makefile4
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cfl-match.c18
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cml-match.c56
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cnl-match.c31
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-jsl-match.c18
-rw-r--r--sound/soc/intel/haswell/sst-haswell-pcm.c62
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c67
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c6
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c52
-rw-r--r--sound/soc/mediatek/Kconfig1
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.c28
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.h10
-rw-r--r--sound/soc/mediatek/common/mtk-btcvsd.c76
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-pcm.c11
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-pcm.c26
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c70
-rw-r--r--sound/soc/meson/axg-fifo.c56
-rw-r--r--sound/soc/meson/axg-fifo.h20
-rw-r--r--sound/soc/meson/axg-frddr.c24
-rw-r--r--sound/soc/meson/axg-toddr.c24
-rw-r--r--sound/soc/pxa/mmp-pcm.c62
-rw-r--r--sound/soc/pxa/poodle.c2
-rw-r--r--sound/soc/pxa/pxa-ssp.c14
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c14
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c14
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c14
-rw-r--r--sound/soc/qcom/lpass-platform.c70
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c106
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c55
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.h15
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.c14
-rw-r--r--sound/soc/rockchip/Kconfig3
-rw-r--r--sound/soc/rockchip/rockchip_max98090.c313
-rw-r--r--sound/soc/samsung/Kconfig12
-rw-r--r--sound/soc/samsung/Makefile4
-rw-r--r--sound/soc/samsung/arndale.c217
-rw-r--r--sound/soc/samsung/arndale_rt5631.c164
-rw-r--r--sound/soc/samsung/idma.c58
-rw-r--r--sound/soc/sh/dma-sh7760.c46
-rw-r--r--sound/soc/sh/fsi.c31
-rw-r--r--sound/soc/sh/rcar/core.c34
-rw-r--r--sound/soc/sh/rcar/dma.c30
-rw-r--r--sound/soc/sh/siu_pcm.c44
-rw-r--r--sound/soc/soc-component.c125
-rw-r--r--sound/soc/soc-compress.c52
-rw-r--r--sound/soc/soc-core.c1169
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c165
-rw-r--r--sound/soc/soc-ops.c11
-rw-r--r--sound/soc/soc-pcm.c235
-rw-r--r--sound/soc/soc-topology.c17
-rw-r--r--sound/soc/soc-utils.c11
-rw-r--r--sound/soc/sof/Kconfig43
-rw-r--r--sound/soc/sof/control.c34
-rw-r--r--sound/soc/sof/core.c44
-rw-r--r--sound/soc/sof/debug.c16
-rw-r--r--sound/soc/sof/imx/Kconfig8
-rw-r--r--sound/soc/sof/imx/imx8.c7
-rw-r--r--sound/soc/sof/intel/Kconfig50
-rw-r--r--sound/soc/sof/intel/apl.c8
-rw-r--r--sound/soc/sof/intel/bdw.c31
-rw-r--r--sound/soc/sof/intel/byt.c44
-rw-r--r--sound/soc/sof/intel/cnl.c67
-rw-r--r--sound/soc/sof/intel/hda-codec.c22
-rw-r--r--sound/soc/sof/intel/hda-dsp.c137
-rw-r--r--sound/soc/sof/intel/hda-ipc.c6
-rw-r--r--sound/soc/sof/intel/hda-ipc.h51
-rw-r--r--sound/soc/sof/intel/hda-loader.c25
-rw-r--r--sound/soc/sof/intel/hda-pcm.c15
-rw-r--r--sound/soc/sof/intel/hda-stream.c24
-rw-r--r--sound/soc/sof/intel/hda.c25
-rw-r--r--sound/soc/sof/intel/hda.h23
-rw-r--r--sound/soc/sof/ipc.c10
-rw-r--r--sound/soc/sof/ops.h10
-rw-r--r--sound/soc/sof/pcm.c135
-rw-r--r--sound/soc/sof/pm.c130
-rw-r--r--sound/soc/sof/sof-acpi-dev.c12
-rw-r--r--sound/soc/sof/sof-pci-dev.c88
-rw-r--r--sound/soc/sof/sof-priv.h54
-rw-r--r--sound/soc/sof/topology.c498
-rw-r--r--sound/soc/sof/trace.c17
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.c74
-rw-r--r--sound/soc/stm/stm32_adfsdm.c42
-rw-r--r--sound/soc/stm/stm32_sai.c2
-rw-r--r--sound/soc/stm/stm32_spdifrx.c18
-rw-r--r--sound/soc/sunxi/sun4i-codec.c6
-rw-r--r--sound/soc/tegra/tegra30_i2s.c56
-rw-r--r--sound/soc/ti/davinci-mcasp.c2
-rw-r--r--sound/soc/txx9/txx9aclc.c48
-rw-r--r--sound/soc/uniphier/aio-dma.c51
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c3
-rw-r--r--sound/soc/xilinx/xlnx_formatter_pcm.c63
-rw-r--r--sound/soc/xtensa/xtfpga-i2s.c30
228 files changed, 12215 insertions, 3527 deletions
diff --git a/Documentation/devicetree/bindings/sound/adi,adau7118.yaml b/Documentation/devicetree/bindings/sound/adi,adau7118.yaml
new file mode 100644
index 000000000000..75e0cbe6be70
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/adi,adau7118.yaml
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/adi,adau7118.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+
+title: Analog Devices ADAU7118 8 Channel PDM to I2S/TDM Converter
+
+maintainers:
+ - Nuno Sá <nuno.sa@analog.com>
+
+description: |
+ Analog Devices ADAU7118 8 Channel PDM to I2S/TDM Converter over I2C or HW
+ standalone mode.
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ADAU7118.pdf
+
+properties:
+ compatible:
+ enum:
+ - adi,adau7118
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+ iovdd-supply:
+ description: Digital Input/Output Power Supply.
+
+ dvdd-supply:
+ description: Internal Core Digital Power Supply.
+
+ adi,decimation-ratio:
+ description: |
+ This property set's the decimation ratio of PDM to PCM audio data.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - enum: [64, 32, 16]
+ default: 64
+
+ adi,pdm-clk-map:
+ description: |
+ The ADAU7118 has two PDM clocks for the four Inputs. Each input must be
+ assigned to one of these two clocks. This property set's the mapping
+ between the clocks and the inputs.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32-array
+ - minItems: 4
+ maxItems: 4
+ items:
+ maximum: 1
+ default: [0, 0, 1, 1]
+
+required:
+ - "#sound-dai-cells"
+ - compatible
+ - iovdd-supply
+ - dvdd-supply
+
+examples:
+ - |
+ i2c {
+ /* example with i2c support */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ adau7118_codec: audio-codec@14 {
+ compatible = "adi,adau7118";
+ reg = <0x14>;
+ #sound-dai-cells = <0>;
+ iovdd-supply = <&supply>;
+ dvdd-supply = <&supply>;
+ adi,pdm-clk-map = <1 1 0 0>;
+ adi,decimation-ratio = <16>;
+ };
+ };
+
+ /* example with hw standalone mode */
+ adau7118_codec_hw: adau7118-codec-hw {
+ compatible = "adi,adau7118";
+ #sound-dai-cells = <0>;
+ iovdd-supply = <&supply>;
+ dvdd-supply = <&supply>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml
new file mode 100644
index 000000000000..b8f89c7258eb
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml
@@ -0,0 +1,267 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/allwinner,sun4i-a10-codec.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A10 Codec Device Tree Bindings
+
+maintainers:
+ - Chen-Yu Tsai <wens@csie.org>
+ - Maxime Ripard <maxime.ripard@bootlin.com>
+
+properties:
+ "#sound-dai-cells":
+ const: 0
+
+ compatible:
+ enum:
+ - allwinner,sun4i-a10-codec
+ - allwinner,sun6i-a31-codec
+ - allwinner,sun7i-a20-codec
+ - allwinner,sun8i-a23-codec
+ - allwinner,sun8i-h3-codec
+ - allwinner,sun8i-v3s-codec
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Bus Clock
+ - description: Module Clock
+
+ clock-names:
+ items:
+ - const: apb
+ - const: codec
+
+ dmas:
+ items:
+ - description: RX DMA Channel
+ - description: TX DMA Channel
+
+ dma-names:
+ items:
+ - const: rx
+ - const: tx
+
+ resets:
+ maxItems: 1
+
+ allwinner,audio-routing:
+ 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.
+ allOf:
+ - $ref: /schemas/types.yaml#definitions/non-unique-string-array
+ - minItems: 2
+ maxItems: 18
+ items:
+ enum:
+ # Audio Pins on the SoC
+ - HP
+ - HPCOM
+ - LINEIN
+ - LINEOUT
+ - MIC1
+ - MIC2
+ - MIC3
+
+ # Microphone Biases from the SoC
+ - HBIAS
+ - MBIAS
+
+ # Board Connectors
+ - Headphone
+ - Headset Mic
+ - Line In
+ - Line Out
+ - Mic
+ - Speaker
+
+ allwinner,codec-analog-controls:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: Phandle to the codec analog controls in the PRCM
+
+ allwinner,pa-gpios:
+ description: GPIO to enable the external amplifier
+
+required:
+ - "#sound-dai-cells"
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - dmas
+ - dma-names
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ enum:
+ - allwinner,sun6i-a31-codec
+ - allwinner,sun8i-a23-codec
+ - allwinner,sun8i-h3-codec
+ - allwinner,sun8i-v3s-codec
+
+ then:
+ if:
+ properties:
+ compatible:
+ const: allwinner,sun6i-a31-codec
+
+ then:
+ required:
+ - resets
+ - allwinner,audio-routing
+
+ else:
+ required:
+ - resets
+ - allwinner,audio-routing
+ - allwinner,codec-analog-controls
+
+ - if:
+ properties:
+ compatible:
+ enum:
+ - allwinner,sun6i-a31-codec
+
+ then:
+ properties:
+ allwinner,audio-routing:
+ items:
+ enum:
+ - HP
+ - HPCOM
+ - LINEIN
+ - LINEOUT
+ - MIC1
+ - MIC2
+ - MIC3
+ - HBIAS
+ - MBIAS
+ - Headphone
+ - Headset Mic
+ - Line In
+ - Line Out
+ - Mic
+ - Speaker
+
+ - if:
+ properties:
+ compatible:
+ enum:
+ - allwinner,sun8i-a23-codec
+
+ then:
+ properties:
+ allwinner,audio-routing:
+ items:
+ enum:
+ - HP
+ - HPCOM
+ - LINEIN
+ - MIC1
+ - MIC2
+ - HBIAS
+ - MBIAS
+ - Headphone
+ - Headset Mic
+ - Line In
+ - Line Out
+ - Mic
+ - Speaker
+
+ - if:
+ properties:
+ compatible:
+ enum:
+ - allwinner,sun8i-h3-codec
+
+ then:
+ properties:
+ allwinner,audio-routing:
+ items:
+ enum:
+ - HP
+ - HPCOM
+ - LINEIN
+ - LINEOUT
+ - MIC1
+ - MIC2
+ - HBIAS
+ - MBIAS
+ - Headphone
+ - Headset Mic
+ - Line In
+ - Line Out
+ - Mic
+ - Speaker
+
+ - if:
+ properties:
+ compatible:
+ enum:
+ - allwinner,sun8i-v3s-codec
+
+ then:
+ properties:
+ allwinner,audio-routing:
+ items:
+ enum:
+ - HP
+ - HPCOM
+ - MIC1
+ - HBIAS
+ - Headphone
+ - Headset Mic
+ - Line In
+ - Line Out
+ - Mic
+ - Speaker
+
+additionalProperties: false
+
+examples:
+ - |
+ codec@1c22c00 {
+ #sound-dai-cells = <0>;
+ compatible = "allwinner,sun7i-a20-codec";
+ reg = <0x01c22c00 0x40>;
+ interrupts = <0 30 4>;
+ clocks = <&apb0_gates 0>, <&codec_clk>;
+ clock-names = "apb", "codec";
+ dmas = <&dma 0 19>, <&dma 0 19>;
+ dma-names = "rx", "tx";
+ };
+
+ - |
+ codec@1c22c00 {
+ #sound-dai-cells = <0>;
+ compatible = "allwinner,sun6i-a31-codec";
+ reg = <0x01c22c00 0x98>;
+ interrupts = <0 29 4>;
+ clocks = <&ccu 61>, <&ccu 135>;
+ clock-names = "apb", "codec";
+ resets = <&ccu 42>;
+ dmas = <&dma 15>, <&dma 15>;
+ dma-names = "rx", "tx";
+ allwinner,audio-routing =
+ "Headphone", "HP",
+ "Speaker", "LINEOUT",
+ "LINEIN", "Line In",
+ "MIC1", "MBIAS",
+ "MIC1", "Mic",
+ "MIC2", "HBIAS",
+ "MIC2", "Headset Mic";
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml
new file mode 100644
index 000000000000..85305b4c2729
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/allwinner,sun8i-a23-codec-analog.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A23 Analog Codec Device Tree Bindings
+
+maintainers:
+ - Chen-Yu Tsai <wens@csie.org>
+ - Maxime Ripard <maxime.ripard@bootlin.com>
+
+properties:
+ compatible:
+ enum:
+ # FIXME: This is documented in the PRCM binding, but needs to be
+ # migrated here at some point
+ # - allwinner,sun8i-a23-codec-analog
+ - allwinner,sun8i-h3-codec-analog
+ - allwinner,sun8i-v3s-codec-analog
+
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ codec_analog: codec-analog@1f015c0 {
+ compatible = "allwinner,sun8i-h3-codec-analog";
+ reg = <0x01f015c0 0x4>;
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/arndale.txt b/Documentation/devicetree/bindings/sound/arndale.txt
index 0e76946385ae..17530120ccfc 100644
--- a/Documentation/devicetree/bindings/sound/arndale.txt
+++ b/Documentation/devicetree/bindings/sound/arndale.txt
@@ -1,8 +1,9 @@
Audio Binding for Arndale boards
Required properties:
-- compatible : Can be the following,
- "samsung,arndale-rt5631"
+- compatible : Can be one of the following:
+ "samsung,arndale-rt5631",
+ "samsung,arndale-wm1811"
- samsung,audio-cpu: The phandle of the Samsung I2S controller
- samsung,audio-codec: The phandle of the audio codec
diff --git a/Documentation/devicetree/bindings/sound/fsl,mqs.txt b/Documentation/devicetree/bindings/sound/fsl,mqs.txt
new file mode 100644
index 000000000000..40353fc30255
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,mqs.txt
@@ -0,0 +1,36 @@
+fsl,mqs audio CODEC
+
+Required properties:
+ - compatible : Must contain one of "fsl,imx6sx-mqs", "fsl,codec-mqs"
+ "fsl,imx8qm-mqs", "fsl,imx8qxp-mqs".
+ - clocks : A list of phandles + clock-specifiers, one for each entry in
+ clock-names
+ - clock-names : "mclk" - must required.
+ "core" - required if compatible is "fsl,imx8qm-mqs", it
+ is for register access.
+ - gpr : A phandle of General Purpose Registers in IOMUX Controller.
+ Required if compatible is "fsl,imx6sx-mqs".
+
+Required if compatible is "fsl,imx8qm-mqs":
+ - power-domains: A phandle of PM domain provider node.
+ - reg: Offset and length of the register set for the device.
+
+Example:
+
+mqs: mqs {
+ compatible = "fsl,imx6sx-mqs";
+ gpr = <&gpr>;
+ clocks = <&clks IMX6SX_CLK_SAI1>;
+ clock-names = "mclk";
+ status = "disabled";
+};
+
+mqs: mqs@59850000 {
+ compatible = "fsl,imx8qm-mqs";
+ reg = <0x59850000 0x10000>;
+ clocks = <&clk IMX8QM_AUD_MQS_IPG>,
+ <&clk IMX8QM_AUD_MQS_HMCLK>;
+ clock-names = "core", "mclk";
+ power-domains = <&pd_mqs0>;
+ status = "disabled";
+};
diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt
index 1084f7f22eea..8ca52dcc5572 100644
--- a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt
+++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt
@@ -1,4 +1,4 @@
-* Audio codec controlled by ChromeOS EC
+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.
@@ -9,10 +9,27 @@ 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.
-- max-dmic-gain: A number for maximum gain in dB on digital microphone.
+
+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";
@@ -21,6 +38,7 @@ cros-ec@0 {
cros_ec_codec: ec-codec {
compatible = "google,cros-ec-codec";
#sound-dai-cells = <1>;
- max-dmic-gain = <43>;
+ reg = <0x0 0x10500000 0x80000>;
+ memory-region = <&reserved_mem>;
};
};
diff --git a/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt
index 396ba38619f6..1f1cba4152ce 100644
--- a/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt
+++ b/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt
@@ -4,6 +4,10 @@ Required properties:
- compatible = "mediatek,mt68183-audio";
- reg: register location and size
- interrupts: should contain AFE interrupt
+- resets: Must contain an entry for each entry in reset-names
+ See ../reset/reset.txt for details.
+- reset-names: should have these reset names:
+ "audiosys";
- power-domains: should define the power domain
- clocks: Must contain an entry for each entry in clock-names
- clock-names: should have these clock names:
@@ -20,6 +24,8 @@ Example:
compatible = "mediatek,mt8183-audio";
reg = <0 0x11220000 0 0x1000>;
interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_LOW>;
+ resets = <&watchdog MT8183_TOPRGU_AUDIO_SW_RST>;
+ reset-names = "audiosys";
power-domains = <&scpsys MT8183_POWER_DOMAIN_AUDIO>;
clocks = <&infrasys CLK_INFRA_AUDIO>,
<&infrasys CLK_INFRA_AUDIO_26M_BCLK>,
diff --git a/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt b/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt
index d6d5207fa996..decaa013a07e 100644
--- a/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt
+++ b/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt
@@ -2,14 +2,19 @@ MT8183 with MT6358, TS3A227 and MAX98357 CODECS
Required properties:
- compatible : "mediatek,mt8183_mt6358_ts3a227_max98357"
-- mediatek,headset-codec: the phandles of ts3a227 codecs
- mediatek,platform: the phandle of MT8183 ASoC platform
+Optional properties:
+- mediatek,headset-codec: the phandles of ts3a227 codecs
+- mediatek,ec-codec: the phandle of EC codecs.
+ See google,cros-ec-codec.txt for more details.
+
Example:
sound {
compatible = "mediatek,mt8183_mt6358_ts3a227_max98357";
mediatek,headset-codec = <&ts3a227>;
+ mediatek,ec-codec = <&ec_codec>;
mediatek,platform = <&afe>;
};
diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt
deleted file mode 100644
index 0cf0f819b823..000000000000
--- a/Documentation/devicetree/bindings/sound/renesas,fsi.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-Renesas FSI
-
-Required properties:
-- compatible : "renesas,fsi2-<soctype>",
- "renesas,sh_fsi2" or "renesas,sh_fsi" as
- fallback.
- Examples with soctypes are:
- - "renesas,fsi2-r8a7740" (R-Mobile A1)
- - "renesas,fsi2-sh73a0" (SH-Mobile AG5)
-- reg : Should contain the register physical address and length
-- interrupts : Should contain FSI interrupt
-
-- fsia,spdif-connection : FSI is connected by S/PDIF
-- fsia,stream-mode-support : FSI supports 16bit stream mode.
-- fsia,use-internal-clock : FSI uses internal clock when master mode.
-
-- fsib,spdif-connection : same as fsia
-- fsib,stream-mode-support : same as fsia
-- fsib,use-internal-clock : same as fsia
-
-Example:
-
-sh_fsi2: sh_fsi2@ec230000 {
- compatible = "renesas,sh_fsi2";
- reg = <0xec230000 0x400>;
- interrupts = <0 146 0x4>;
-
- fsia,spdif-connection;
- fsia,stream-mode-support;
- fsia,use-internal-clock;
-};
diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.yaml b/Documentation/devicetree/bindings/sound/renesas,fsi.yaml
new file mode 100644
index 000000000000..140a37fc3c0b
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/renesas,fsi.yaml
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/renesas,fsi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas FSI Sound Driver Device Tree Bindings
+
+maintainers:
+ - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+properties:
+ $nodename:
+ pattern: "^sound@.*"
+
+ compatible:
+ oneOf:
+ # for FSI2 SoC
+ - items:
+ - enum:
+ - renesas,fsi2-sh73a0
+ - renesas,fsi2-r8a7740
+ - enum:
+ - renesas,sh_fsi2
+ # for Generic
+ - items:
+ - enum:
+ - renesas,sh_fsi
+ - renesas,sh_fsi2
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ fsia,spdif-connection:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: FSI is connected by S/PDIF
+
+ fsia,stream-mode-support:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: FSI supports 16bit stream mode
+
+ fsia,use-internal-clock:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: FSI uses internal clock when master mode
+
+ fsib,spdif-connection:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: same as fsia
+
+ fsib,stream-mode-support:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: same as fsia
+
+ fsib,use-internal-clock:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: same as fsia
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+examples:
+ - |
+ sh_fsi2: sound@ec230000 {
+ compatible = "renesas,fsi2-r8a7740", "renesas,sh_fsi2";
+ reg = <0xec230000 0x400>;
+ interrupts = <0 146 0x4>;
+
+ fsia,spdif-connection;
+ fsia,stream-mode-support;
+ fsia,use-internal-clock;
+ };
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
index 5c52182f7dcf..797fd035434c 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
@@ -268,6 +268,7 @@ Required properties:
- "renesas,rcar_sound-r8a7745" (RZ/G1E)
- "renesas,rcar_sound-r8a77470" (RZ/G1C)
- "renesas,rcar_sound-r8a774a1" (RZ/G2M)
+ - "renesas,rcar_sound-r8a774b1" (RZ/G2N)
- "renesas,rcar_sound-r8a774c0" (RZ/G2E)
- "renesas,rcar_sound-r8a7778" (R-Car M1A)
- "renesas,rcar_sound-r8a7779" (R-Car H1)
diff --git a/Documentation/devicetree/bindings/sound/rockchip-max98090.txt b/Documentation/devicetree/bindings/sound/rockchip-max98090.txt
index a805aa99ad75..e9c58b204399 100644
--- a/Documentation/devicetree/bindings/sound/rockchip-max98090.txt
+++ b/Documentation/devicetree/bindings/sound/rockchip-max98090.txt
@@ -5,15 +5,38 @@ Required properties:
- rockchip,model: The user-visible name of this sound complex
- rockchip,i2s-controller: The phandle of the Rockchip I2S controller that's
connected to the CODEC
-- rockchip,audio-codec: The phandle of the MAX98090 audio codec
-- rockchip,headset-codec: The phandle of Ext chip for jack detection
+
+Optional properties:
+- rockchip,audio-codec: The phandle of the MAX98090 audio codec.
+- rockchip,headset-codec: The phandle of Ext chip for jack detection. This is
+ required if there is rockchip,audio-codec.
+- rockchip,hdmi-codec: The phandle of HDMI device for HDMI codec.
Example:
+/* For max98090-only board. */
+sound {
+ compatible = "rockchip,rockchip-audio-max98090";
+ rockchip,model = "ROCKCHIP-I2S";
+ rockchip,i2s-controller = <&i2s>;
+ rockchip,audio-codec = <&max98090>;
+ rockchip,headset-codec = <&headsetcodec>;
+};
+
+/* For HDMI-only board. */
+sound {
+ compatible = "rockchip,rockchip-audio-max98090";
+ rockchip,model = "ROCKCHIP-I2S";
+ rockchip,i2s-controller = <&i2s>;
+ rockchip,hdmi-codec = <&hdmi>;
+};
+
+/* For max98090 plus HDMI board. */
sound {
compatible = "rockchip,rockchip-audio-max98090";
rockchip,model = "ROCKCHIP-I2S";
rockchip,i2s-controller = <&i2s>;
rockchip,audio-codec = <&max98090>;
rockchip,headset-codec = <&headsetcodec>;
+ rockchip,hdmi-codec = <&hdmi>;
};
diff --git a/Documentation/devicetree/bindings/sound/rt1011.txt b/Documentation/devicetree/bindings/sound/rt1011.txt
index 35a23e60d679..02d53b9aa247 100644
--- a/Documentation/devicetree/bindings/sound/rt1011.txt
+++ b/Documentation/devicetree/bindings/sound/rt1011.txt
@@ -20,6 +20,14 @@ Required properties:
| 1 | 1 | 0x3b |
-------------------------------------
+Optional properties:
+
+- realtek,temperature_calib
+ u32. The temperature was measured while doing the calibration. Units: Celsius degree
+
+- realtek,r0_calib
+ u32. This is r0 calibration data which was measured in factory mode.
+
Pins on the device (for linking into audio routes) for RT1011:
* SPO
@@ -29,4 +37,6 @@ Example:
rt1011: codec@38 {
compatible = "realtek,rt1011";
reg = <0x38>;
+ realtek,temperature_calib = <25>;
+ realtek,r0_calib = <0x224050>;
};
diff --git a/Documentation/devicetree/bindings/sound/rt5682.txt b/Documentation/devicetree/bindings/sound/rt5682.txt
index 312e9a129530..30e927a28369 100644
--- a/Documentation/devicetree/bindings/sound/rt5682.txt
+++ b/Documentation/devicetree/bindings/sound/rt5682.txt
@@ -27,6 +27,11 @@ Optional properties:
- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
+- realtek,btndet-delay
+ The debounce delay for push button.
+ The delay time is realtek,btndet-delay value multiple of 8.192 ms.
+ If absent, the default is 16.
+
Pins on the device (for linking into audio routes) for RT5682:
* DMIC L1
@@ -47,4 +52,5 @@ rt5682 {
realtek,dmic1-data-pin = <1>;
realtek,dmic1-clk-pin = <1>;
realtek,jd-src = <1>;
+ realtek,btndet-delay = <16>;
};
diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.txt b/Documentation/devicetree/bindings/sound/samsung,odroid.txt
deleted file mode 100644
index e9da2200e173..000000000000
--- a/Documentation/devicetree/bindings/sound/samsung,odroid.txt
+++ /dev/null
@@ -1,54 +0,0 @@
-Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec
-
-Required properties:
-
- - compatible - "hardkernel,odroid-xu3-audio" - for Odroid XU3 board,
- "hardkernel,odroid-xu4-audio" - for Odroid XU4 board (deprecated),
- "samsung,odroid-xu3-audio" - for Odroid XU3 board (deprecated),
- "samsung,odroid-xu4-audio" - for Odroid XU4 board (deprecated)
- - model - the user-visible name of this sound complex
- - clocks - should contain entries matching clock names in the clock-names
- property
- - samsung,audio-widgets - this property specifies off-codec audio elements
- like headphones or speakers, for details see widgets.txt
- - samsung,audio-routing - a list of the connections between audio
- components; each entry is a pair of strings, the first being the
- connection's sink, the second being the connection's source;
- valid names for sources and sinks are the MAX98090's pins (as
- documented in its binding), and the jacks on the board
-
- For Odroid X2:
- "Headphone Jack", "Mic Jack", "DMIC"
-
- For Odroid U3, XU3:
- "Headphone Jack", "Speakers"
-
- For Odroid XU4:
- no entries
-
-Required sub-nodes:
-
- - 'cpu' subnode with a 'sound-dai' property containing the phandle of the I2S
- controller
- - 'codec' subnode with a 'sound-dai' property containing list of phandles
- to the CODEC nodes, first entry must be corresponding to the MAX98090
- CODEC and the second entry must be the phandle of the HDMI IP block node
-
-Example:
-
-sound {
- compatible = "hardkernel,odroid-xu3-audio";
- model = "Odroid-XU3";
- samsung,audio-routing =
- "Headphone Jack", "HPL",
- "Headphone Jack", "HPR",
- "IN1", "Mic Jack",
- "Mic Jack", "MICBIAS";
-
- cpu {
- sound-dai = <&i2s0 0>;
- };
- codec {
- sound-dai = <&hdmi>, <&max98090>;
- };
-};
diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.yaml b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml
new file mode 100644
index 000000000000..c6b244352d05
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/samsung,odroid.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec
+
+maintainers:
+ - Krzysztof Kozlowski <krzk@kernel.org>
+ - Sylwester Nawrocki <s.nawrocki@samsung.com>
+
+properties:
+ compatible:
+ oneOf:
+ - const: hardkernel,odroid-xu3-audio
+
+ - const: hardkernel,odroid-xu4-audio
+ deprecated: true
+
+ - const: samsung,odroid-xu3-audio
+ deprecated: true
+
+ - const: samsung,odroid-xu4-audio
+ deprecated: true
+
+ model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: The user-visible name of this sound complex.
+
+ cpu:
+ type: object
+ properties:
+ sound-dai:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: phandles to the I2S controllers
+
+ codec:
+ type: object
+ properties:
+ sound-dai:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: |
+ List of phandles to the CODEC nodes,
+ first entry must be corresponding to the MAX98090 CODEC and
+ the second entry must be the phandle of the HDMI IP block node.
+
+ samsung,audio-routing:
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ description: |
+ List of the connections between audio
+ components; each entry is a pair of strings, the first being the
+ connection's sink, the second being the connection's source;
+ valid names for sources and sinks are the MAX98090's pins (as
+ documented in its binding), and the jacks on the board.
+ For Odroid X2: "Headphone Jack", "Mic Jack", "DMIC"
+ For Odroid U3, XU3: "Headphone Jack", "Speakers"
+ For Odroid XU4: no entries
+
+ samsung,audio-widgets:
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ description: |
+ This property specifies off-codec audio elements
+ like headphones or speakers, for details see widgets.txt
+
+required:
+ - compatible
+ - model
+ - cpu
+ - codec
+
+examples:
+ - |
+ sound {
+ compatible = "hardkernel,odroid-xu3-audio";
+ model = "Odroid-XU3";
+ samsung,audio-routing =
+ "Headphone Jack", "HPL",
+ "Headphone Jack", "HPR",
+ "IN1", "Mic Jack",
+ "Mic Jack", "MICBIAS";
+
+ cpu {
+ sound-dai = <&i2s0 0>;
+ };
+
+ codec {
+ sound-dai = <&hdmi>, <&max98090>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt
deleted file mode 100644
index a88cb00fa096..000000000000
--- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt
+++ /dev/null
@@ -1,84 +0,0 @@
-* Samsung I2S controller
-
-Required SoC Specific Properties:
-
-- compatible : should be one of the following.
- - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S.
- - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with
- secondary fifo, s/w reset control and internal mux for root clk src.
- - samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for
- playback, stereo channel capture, secondary fifo using internal
- or external dma, s/w reset control, internal mux for root clk src
- and 7.1 channel TDM support for playback. TDM (Time division multiplexing)
- is to allow transfer of multiple channel audio data on single data line.
- - samsung,exynos7-i2s: with all the available features of exynos5 i2s,
- exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo
- with only external dma and more no.of root clk sampling frequencies.
- - samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports
- stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with
- slightly modified bit offsets.
-
-- reg: physical base address of the controller and length of memory mapped
- region.
-- dmas: list of DMA controller phandle and DMA request line ordered pairs.
-- dma-names: identifier string for each DMA request line in the dmas property.
- These strings correspond 1:1 with the ordered pairs in dmas.
-- clocks: Handle to iis clock and RCLK source clk.
-- clock-names:
- i2s0 uses some base clocks from CMU and some are from audio subsystem internal
- clock controller. The clock names for i2s0 should be "iis", "i2s_opclk0" and
- "i2s_opclk1" as shown in the example below.
- i2s1 and i2s2 uses clocks from CMU. The clock names for i2s1 and i2s2 should
- be "iis" and "i2s_opclk0".
- "iis" is the i2s bus clock and i2s_opclk0, i2s_opclk1 are sources of the root
- clk. i2s0 has internal mux to select the source of root clk and i2s1 and i2s2
- doesn't have any such mux.
-- #clock-cells: should be 1, this property must be present if the I2S device
- is a clock provider in terms of the common clock bindings, described in
- ../clock/clock-bindings.txt.
-- clock-output-names (deprecated): from the common clock bindings, names of
- the CDCLK I2S output clocks, suggested values are "i2s_cdclk0", "i2s_cdclk1",
- "i2s_cdclk3" for the I2S0, I2S1, I2S2 devices respectively.
-
-There are following clocks available at the I2S device nodes:
- CLK_I2S_CDCLK - the CDCLK (CODECLKO) gate clock,
- CLK_I2S_RCLK_PSR - the RCLK prescaler divider clock (corresponding to the
- IISPSR register),
- CLK_I2S_RCLK_SRC - the RCLKSRC mux clock (corresponding to RCLKSRC bit in
- IISMOD register).
-
-Refer to the SoC datasheet for availability of the above clocks.
-The CLK_I2S_RCLK_PSR and CLK_I2S_RCLK_SRC clocks are usually only available
-in the IIS Multi Audio Interface.
-
-Note: Old DTs may not have the #clock-cells property and then not use the I2S
-node as a clock supplier.
-
-Optional SoC Specific Properties:
-
-- samsung,idma-addr: Internal DMA register base address of the audio
- sub system(used in secondary sound source).
-- pinctrl-0: Should specify pin control groups used for this controller.
-- pinctrl-names: Should contain only one value - "default".
-- #sound-dai-cells: should be 1.
-
-
-Example:
-
-i2s0: i2s@3830000 {
- compatible = "samsung,s5pv210-i2s";
- reg = <0x03830000 0x100>;
- dmas = <&pdma0 10
- &pdma0 9
- &pdma0 8>;
- dma-names = "tx", "rx", "tx-sec";
- clocks = <&clock_audss EXYNOS_I2S_BUS>,
- <&clock_audss EXYNOS_I2S_BUS>,
- <&clock_audss EXYNOS_SCLK_I2S>;
- clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
- #clock-cells = <1>;
- samsung,idma-addr = <0x03000000>;
- pinctrl-names = "default";
- pinctrl-0 = <&i2s0_bus>;
- #sound-dai-cells = <1>;
-};
diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.yaml b/Documentation/devicetree/bindings/sound/samsung-i2s.yaml
new file mode 100644
index 000000000000..53e3bad4178c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/samsung-i2s.yaml
@@ -0,0 +1,138 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/samsung-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung SoC I2S controller
+
+maintainers:
+ - Krzysztof Kozlowski <krzk@kernel.org>
+ - Sylwester Nawrocki <s.nawrocki@samsung.com>
+
+properties:
+ compatible:
+ description: |
+ samsung,s3c6410-i2s: for 8/16/24bit stereo I2S.
+
+ samsung,s5pv210-i2s: for 8/16/24bit multichannel (5.1) I2S with
+ secondary FIFO, s/w reset control and internal mux for root clock
+ source.
+
+ samsung,exynos5420-i2s: for 8/16/24bit multichannel (5.1) I2S for
+ playback, stereo channel capture, secondary FIFO using internal
+ or external DMA, s/w reset control, internal mux for root clock
+ source and 7.1 channel TDM support for playback; TDM (Time division
+ multiplexing) is to allow transfer of multiple channel audio data on
+ single data line.
+
+ samsung,exynos7-i2s: with all the available features of Exynos5 I2S.
+ Exynos7 I2S has 7.1 channel TDM support for capture, secondary FIFO
+ with only external DMA and more number of root clock sampling
+ frequencies.
+
+ samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports
+ stereo channels. Exynos7 I2S1 upgraded to 5.1 multichannel with
+ slightly modified bit offsets.
+ enum:
+ - samsung,s3c6410-i2s
+ - samsung,s5pv210-i2s
+ - samsung,exynos5420-i2s
+ - samsung,exynos7-i2s
+ - samsung,exynos7-i2s1
+
+ reg:
+ maxItems: 1
+
+ dmas:
+ minItems: 2
+ maxItems: 3
+
+ dma-names:
+ oneOf:
+ - items:
+ - const: tx
+ - const: rx
+ - items:
+ - const: tx
+ - const: rx
+ - const: tx-sec
+
+ clocks:
+ minItems: 1
+ maxItems: 3
+
+ clock-names:
+ oneOf:
+ - items:
+ - const: iis
+ - items: # for I2S0
+ - const: iis
+ - const: i2s_opclk0
+ - const: i2s_opclk1
+ - items: # for I2S1 and I2S2
+ - const: iis
+ - const: i2s_opclk0
+ description: |
+ "iis" is the I2S bus clock and i2s_opclk0, i2s_opclk1 are sources
+ of the root clock. I2S0 has internal mux to select the source
+ of root clock and I2S1 and I2S2 doesn't have any such mux.
+
+ "#clock-cells":
+ const: 1
+
+ clock-output-names:
+ deprecated: true
+ oneOf:
+ - items: # for I2S0
+ - const: i2s_cdclk0
+ - items: # for I2S1
+ - const: i2s_cdclk1
+ - items: # for I2S2
+ - const: i2s_cdclk2
+ description: Names of the CDCLK I2S output clocks.
+
+ samsung,idma-addr:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Internal DMA register base address of the audio
+ subsystem (used in secondary sound source).
+
+ pinctrl-0:
+ description: Should specify pin control groups used for this controller.
+
+ pinctrl-names:
+ const: default
+
+ "#sound-dai-cells":
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - dmas
+ - dma-names
+ - clocks
+ - clock-names
+
+examples:
+ - |
+ #include <dt-bindings/clock/exynos-audss-clk.h>
+
+ i2s0: i2s@3830000 {
+ compatible = "samsung,s5pv210-i2s";
+ reg = <0x03830000 0x100>;
+ dmas = <&pdma0 10>,
+ <&pdma0 9>,
+ <&pdma0 8>;
+ dma-names = "tx", "rx", "tx-sec";
+ clocks = <&clock_audss EXYNOS_I2S_BUS>,
+ <&clock_audss EXYNOS_I2S_BUS>,
+ <&clock_audss EXYNOS_SCLK_I2S>;
+ clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
+ #clock-cells = <1>;
+ samsung,idma-addr = <0x03000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s0_bus>;
+ #sound-dai-cells = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
deleted file mode 100644
index 66579bbd3294..000000000000
--- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt
+++ /dev/null
@@ -1,94 +0,0 @@
-* Allwinner A10 Codec
-
-Required properties:
-- compatible: must be one of the following compatibles:
- - "allwinner,sun4i-a10-codec"
- - "allwinner,sun6i-a31-codec"
- - "allwinner,sun7i-a20-codec"
- - "allwinner,sun8i-a23-codec"
- - "allwinner,sun8i-h3-codec"
- - "allwinner,sun8i-v3s-codec"
-- reg: must contain the registers location and length
-- interrupts: must contain the codec interrupt
-- dmas: DMA channels for tx and rx dma. See the DMA client binding,
- Documentation/devicetree/bindings/dma/dma.txt
-- dma-names: should include "tx" and "rx".
-- clocks: a list of phandle + clock-specifer pairs, one for each entry
- in clock-names.
-- clock-names: should contain the following:
- - "apb": the parent APB clock for this controller
- - "codec": the parent module clock
-
-Optional properties:
-- allwinner,pa-gpios: gpio to enable external amplifier
-
-Required properties for the following compatibles:
- - "allwinner,sun6i-a31-codec"
- - "allwinner,sun8i-a23-codec"
- - "allwinner,sun8i-h3-codec"
- - "allwinner,sun8i-v3s-codec"
-- resets: phandle to the reset control for this device
-- allwinner,audio-routing: A list of the connections between audio components.
- Each entry is a pair of strings, the first being the
- connection's sink, the second being the connection's
- source. Valid names include:
-
- Audio pins on the SoC:
- "HP"
- "HPCOM"
- "LINEIN" (not on sun8i-v3s)
- "LINEOUT" (not on sun8i-a23 or sun8i-v3s)
- "MIC1"
- "MIC2" (not on sun8i-v3s)
- "MIC3" (sun6i-a31 only)
-
- Microphone biases from the SoC:
- "HBIAS"
- "MBIAS" (not on sun8i-v3s)
-
- Board connectors:
- "Headphone"
- "Headset Mic"
- "Line In"
- "Line Out"
- "Mic"
- "Speaker"
-
-Required properties for the following compatibles:
- - "allwinner,sun8i-a23-codec"
- - "allwinner,sun8i-h3-codec"
- - "allwinner,sun8i-v3s-codec"
-- allwinner,codec-analog-controls: A phandle to the codec analog controls
- block in the PRCM.
-
-Example:
-codec: codec@1c22c00 {
- #sound-dai-cells = <0>;
- compatible = "allwinner,sun7i-a20-codec";
- reg = <0x01c22c00 0x40>;
- interrupts = <0 30 4>;
- clocks = <&apb0_gates 0>, <&codec_clk>;
- clock-names = "apb", "codec";
- dmas = <&dma 0 19>, <&dma 0 19>;
- dma-names = "rx", "tx";
-};
-
-codec: codec@1c22c00 {
- #sound-dai-cells = <0>;
- compatible = "allwinner,sun6i-a31-codec";
- reg = <0x01c22c00 0x98>;
- interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&ccu CLK_APB1_CODEC>, <&ccu CLK_CODEC>;
- clock-names = "apb", "codec";
- resets = <&ccu RST_APB1_CODEC>;
- dmas = <&dma 15>, <&dma 15>;
- dma-names = "rx", "tx";
- allwinner,audio-routing =
- "Headphone", "HP",
- "Speaker", "LINEOUT",
- "LINEIN", "Line In",
- "MIC1", "MBIAS",
- "MIC1", "Mic",
- "MIC2", "HBIAS",
- "MIC2", "Headset Mic";
-};
diff --git a/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt b/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt
deleted file mode 100644
index 07356758bd91..000000000000
--- a/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-* Allwinner Codec Analog Controls
-
-Required properties:
-- compatible: must be one of the following compatibles:
- - "allwinner,sun8i-a23-codec-analog"
- - "allwinner,sun8i-h3-codec-analog"
- - "allwinner,sun8i-v3s-codec-analog"
-
-Required properties if not a sub-node of the PRCM node:
-- reg: must contain the registers location and length
-
-Example:
-prcm: prcm@1f01400 {
- codec_analog: codec-analog {
- compatible = "allwinner,sun8i-a23-codec-analog";
- };
-};
diff --git a/Documentation/devicetree/bindings/sound/tas2562.txt b/Documentation/devicetree/bindings/sound/tas2562.txt
new file mode 100644
index 000000000000..658e1fb18a99
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/tas2562.txt
@@ -0,0 +1,34 @@
+Texas Instruments TAS2562 Smart PA
+
+The TAS2562 is a mono, digital input Class-D audio amplifier optimized for
+efficiently driving high peak power into small loudspeakers.
+Integrated speaker voltage and current sense provides for
+real time monitoring of loudspeaker behavior.
+
+Required properties:
+ - #address-cells - Should be <1>.
+ - #size-cells - Should be <0>.
+ - compatible: - Should contain "ti,tas2562".
+ - reg: - The i2c address. Should be 0x4c, 0x4d, 0x4e or 0x4f.
+ - ti,imon-slot-no:- TDM TX current sense time slot.
+
+Optional properties:
+- interrupt-parent: phandle to the interrupt controller which provides
+ the interrupt.
+- interrupts: (GPIO) interrupt to which the chip is connected.
+- shut-down: GPIO used to control the state of the device.
+
+Examples:
+tas2562@4c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "ti,tas2562";
+ reg = <0x4c>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <14>;
+
+ shut-down = <&gpio1 15 0>;
+ ti,imon-slot-no = <0>;
+};
+
diff --git a/Documentation/devicetree/bindings/sound/tas2770.txt b/Documentation/devicetree/bindings/sound/tas2770.txt
new file mode 100644
index 000000000000..ede6bb3d9637
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/tas2770.txt
@@ -0,0 +1,37 @@
+Texas Instruments TAS2770 Smart PA
+
+The TAS2770 is a mono, digital input Class-D audio amplifier optimized for
+efficiently driving high peak power into small loudspeakers.
+Integrated speaker voltage and current sense provides for
+real time monitoring of loudspeaker behavior.
+
+Required properties:
+
+ - compatible: - Should contain "ti,tas2770".
+ - reg: - The i2c address. Should contain <0x4c>, <0x4d>,<0x4e>, or <0x4f>.
+ - #address-cells - Should be <1>.
+ - #size-cells - Should be <0>.
+ - ti,asi-format: - Sets TDM RX capture edge. 0->Rising; 1->Falling.
+ - ti,imon-slot-no:- TDM TX current sense time slot.
+ - ti,vmon-slot-no:- TDM TX voltage sense time slot.
+
+Optional properties:
+
+- interrupt-parent: the phandle to the interrupt controller which provides
+ the interrupt.
+- interrupts: interrupt specification for data-ready.
+
+Examples:
+
+ tas2770@4c {
+ compatible = "ti,tas2770";
+ reg = <0x4c>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&msm_gpio>;
+ interrupts = <97 0>;
+ ti,asi-format = <0>;
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+
diff --git a/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt b/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt
index 5d9cb84c661d..a02ecaab5183 100644
--- a/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt
+++ b/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt
@@ -25,6 +25,13 @@ Required properties:
For required properties on SPI/I2C, consult SPI/I2C device tree documentation
+Optional properties:
+
+ - reset-gpios : Optional reset gpio line connected to RST pin of the codec.
+ The RST line is low active:
+ RST = low: device power-down
+ RST = high: device is enabled
+
Examples:
i2c0: i2c0@0 {
@@ -34,6 +41,7 @@ i2c0: i2c0@0 {
pcm3168a: audio-codec@44 {
compatible = "ti,pcm3168a";
reg = <0x44>;
+ reset-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
clocks = <&clk_core CLK_AUDIO>;
clock-names = "scki";
VDD1-supply = <&supply3v3>;
diff --git a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
index 5b3c33bb99e5..e372303697dc 100644
--- a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
+++ b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
@@ -29,6 +29,11 @@ Optional properties:
3 or MICBIAS_AVDD - MICBIAS output is connected to AVDD
If this node is not mentioned or if the value is unknown, then
micbias is set to 2.0V.
+- ai31xx-ocmv - output common-mode voltage setting
+ 0 - 1.35V,
+ 1 - 1.5V,
+ 2 - 1.65V,
+ 3 - 1.8V
Deprecated properties:
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 967e78c5ec0a..67b89db54aa2 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -16,7 +16,7 @@ properties: {}
patternProperties:
# Prefixes which are not vendors, but followed the pattern
# DO NOT ADD NEW PROPERTIES TO THIS LIST
- "^(at25|devbus|dmacap|dsa|exynos|gpio-fan|gpio|gpmc|hdmi|i2c-gpio),.*": true
+ "^(at25|devbus|dmacap|dsa|exynos|fsi[ab]|gpio-fan|gpio|gpmc|hdmi|i2c-gpio),.*": true
"^(keypad|m25p|max8952|max8997|max8998|mpmc),.*": true
"^(pinctrl-single|#pinctrl-single|PowerPC),.*": true
"^(pl022|pxa-mmc|rcar_sound|rotary-encoder|s5m8767|sdhci),.*": true
diff --git a/MAINTAINERS b/MAINTAINERS
index e4f170d8bc29..236bb3630ec2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1002,6 +1002,7 @@ F: drivers/media/i2c/adv7842*
ANALOG DEVICES INC ASOC CODEC DRIVERS
M: Lars-Peter Clausen <lars@metafoo.de>
+M: Nuno Sá <nuno.sa@analog.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
W: http://wiki.analog.com/
W: http://ez.analog.com/community/linux-device-drivers
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
index 1d15cf9b6821..6c2c44d0bdee 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
@@ -151,11 +151,22 @@ static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
return -EINVAL;
}
+static int dw_hdmi_i2s_hook_plugged_cb(struct device *dev, void *data,
+ hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ struct dw_hdmi_i2s_audio_data *audio = data;
+ struct dw_hdmi *hdmi = audio->hdmi;
+
+ return dw_hdmi_set_plugged_cb(hdmi, fn, codec_dev);
+}
+
static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
.hw_params = dw_hdmi_i2s_hw_params,
.audio_shutdown = dw_hdmi_i2s_audio_shutdown,
.get_eld = dw_hdmi_i2s_get_eld,
.get_dai_id = dw_hdmi_i2s_get_dai_id,
+ .hook_plugged_cb = dw_hdmi_i2s_hook_plugged_cb,
};
static int snd_dw_hdmi_probe(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 521d689413c8..2102872bf43c 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -191,6 +191,10 @@ struct dw_hdmi {
struct mutex cec_notifier_mutex;
struct cec_notifier *cec_notifier;
+
+ hdmi_codec_plugged_cb plugged_cb;
+ struct device *codec_dev;
+ enum drm_connector_status last_connector_result;
};
#define HDMI_IH_PHY_STAT0_RX_SENSE \
@@ -215,6 +219,28 @@ static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset)
return val;
}
+static void handle_plugged_change(struct dw_hdmi *hdmi, bool plugged)
+{
+ if (hdmi->plugged_cb && hdmi->codec_dev)
+ hdmi->plugged_cb(hdmi->codec_dev, plugged);
+}
+
+int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ bool plugged;
+
+ mutex_lock(&hdmi->mutex);
+ hdmi->plugged_cb = fn;
+ hdmi->codec_dev = codec_dev;
+ plugged = hdmi->last_connector_result == connector_status_connected;
+ handle_plugged_change(hdmi, plugged);
+ mutex_unlock(&hdmi->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_plugged_cb);
+
static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
{
regmap_update_bits(hdmi->regm, reg << hdmi->reg_shift, mask, data);
@@ -2161,6 +2187,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
{
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
connector);
+ enum drm_connector_status result;
mutex_lock(&hdmi->mutex);
hdmi->force = DRM_FORCE_UNSPECIFIED;
@@ -2168,7 +2195,18 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
dw_hdmi_update_phy_mask(hdmi);
mutex_unlock(&hdmi->mutex);
- return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+ result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+
+ mutex_lock(&hdmi->mutex);
+ if (result != hdmi->last_connector_result) {
+ dev_dbg(hdmi->dev, "read_hpd result: %d", result);
+ handle_plugged_change(hdmi,
+ result == connector_status_connected);
+ hdmi->last_connector_result = result;
+ }
+ mutex_unlock(&hdmi->mutex);
+
+ return result;
}
static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
@@ -2619,6 +2657,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
hdmi->rxsense = true;
hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
hdmi->mc_clkdis = 0x7f;
+ hdmi->last_connector_result = connector_status_disconnected;
mutex_init(&hdmi->mutex);
mutex_init(&hdmi->audio_mutex);
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c
index 6f80ff4532ae..5af1d66d9eca 100644
--- a/drivers/platform/chrome/cros_ec_trace.c
+++ b/drivers/platform/chrome/cros_ec_trace.c
@@ -98,7 +98,10 @@
TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \
TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \
TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \
- TRACE_SYMBOL(EC_CMD_CODEC_I2S), \
+ TRACE_SYMBOL(EC_CMD_EC_CODEC), \
+ TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \
+ TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \
+ TRACE_SYMBOL(EC_CMD_EC_CODEC_WOV), \
TRACE_SYMBOL(EC_CMD_REBOOT_EC), \
TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \
TRACE_SYMBOL(EC_CMD_ACPI_READ), \
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index cf528c289857..9a0c8381a069 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -6,6 +6,8 @@
#ifndef __DW_HDMI__
#define __DW_HDMI__
+#include <sound/hdmi-codec.h>
+
struct drm_connector;
struct drm_display_mode;
struct drm_encoder;
@@ -154,6 +156,8 @@ void dw_hdmi_resume(struct dw_hdmi *hdmi);
void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);
+int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
+ struct device *codec_dev);
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate);
void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt);
void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca);
diff --git a/include/dt-bindings/sound/samsung-i2s.h b/include/dt-bindings/sound/samsung-i2s.h
index 77545f14c379..250de0d6c734 100644
--- a/include/dt-bindings/sound/samsung-i2s.h
+++ b/include/dt-bindings/sound/samsung-i2s.h
@@ -2,8 +2,14 @@
#ifndef _DT_BINDINGS_SAMSUNG_I2S_H
#define _DT_BINDINGS_SAMSUNG_I2S_H
-#define CLK_I2S_CDCLK 0
-#define CLK_I2S_RCLK_SRC 1
-#define CLK_I2S_RCLK_PSR 2
+#define CLK_I2S_CDCLK 0 /* the CDCLK (CODECLKO) gate clock */
+
+#define CLK_I2S_RCLK_SRC 1 /* the RCLKSRC mux clock (corresponding to
+ * RCLKSRC bit in IISMOD register)
+ */
+
+#define CLK_I2S_RCLK_PSR 2 /* the RCLK prescaler divider clock
+ * (corresponding to the IISPSR register)
+ */
#endif /* _DT_BINDINGS_SAMSUNG_I2S_H */
diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h
index 98415686cbfa..69210881ebac 100644
--- a/include/linux/platform_data/cros_ec_commands.h
+++ b/include/linux/platform_data/cros_ec_commands.h
@@ -556,6 +556,9 @@ enum host_event_code {
/* Keyboard recovery combo with hardware reinitialization */
EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT = 30,
+ /* WoV */
+ EC_HOST_EVENT_WOV = 31,
+
/*
* The high bit of the event mask is not used as a host event code. If
* it reads back as set, then the entire event mask should be
@@ -1277,8 +1280,6 @@ enum ec_feature_code {
* MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE.
*/
EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37,
- /* EC supports audio codec. */
- EC_FEATURE_AUDIO_CODEC = 38,
/* The MCU is a System Companion Processor (SCP). */
EC_FEATURE_SCP = 39,
/* The MCU is an Integrated Sensor Hub */
@@ -4468,92 +4469,246 @@ enum mkbp_cec_event {
/*****************************************************************************/
-/* Commands for I2S recording on audio codec. */
+/* Commands for audio codec. */
+#define EC_CMD_EC_CODEC 0x00BC
-#define EC_CMD_CODEC_I2S 0x00BC
-#define EC_WOV_I2S_SAMPLE_RATE 48000
+enum ec_codec_subcmd {
+ EC_CODEC_GET_CAPABILITIES = 0x0,
+ EC_CODEC_GET_SHM_ADDR = 0x1,
+ EC_CODEC_SET_SHM_ADDR = 0x2,
+ EC_CODEC_SUBCMD_COUNT,
+};
-enum ec_codec_i2s_subcmd {
- EC_CODEC_SET_SAMPLE_DEPTH = 0x0,
- EC_CODEC_SET_GAIN = 0x1,
- EC_CODEC_GET_GAIN = 0x2,
- EC_CODEC_I2S_ENABLE = 0x3,
- EC_CODEC_I2S_SET_CONFIG = 0x4,
- EC_CODEC_I2S_SET_TDM_CONFIG = 0x5,
- EC_CODEC_I2S_SET_BCLK = 0x6,
- EC_CODEC_I2S_SUBCMD_COUNT = 0x7,
+enum ec_codec_cap {
+ EC_CODEC_CAP_WOV_AUDIO_SHM = 0,
+ EC_CODEC_CAP_WOV_LANG_SHM = 1,
+ EC_CODEC_CAP_LAST = 32,
};
-enum ec_sample_depth_value {
- EC_CODEC_SAMPLE_DEPTH_16 = 0,
- EC_CODEC_SAMPLE_DEPTH_24 = 1,
+enum ec_codec_shm_id {
+ EC_CODEC_SHM_ID_WOV_AUDIO = 0x0,
+ EC_CODEC_SHM_ID_WOV_LANG = 0x1,
+ EC_CODEC_SHM_ID_LAST,
};
-enum ec_i2s_config {
- EC_DAI_FMT_I2S = 0,
- EC_DAI_FMT_RIGHT_J = 1,
- EC_DAI_FMT_LEFT_J = 2,
- EC_DAI_FMT_PCM_A = 3,
- EC_DAI_FMT_PCM_B = 4,
- EC_DAI_FMT_PCM_TDM = 5,
+enum ec_codec_shm_type {
+ EC_CODEC_SHM_TYPE_EC_RAM = 0x0,
+ EC_CODEC_SHM_TYPE_SYSTEM_RAM = 0x1,
};
-/*
- * For subcommand EC_CODEC_GET_GAIN.
- */
-struct __ec_align1 ec_codec_i2s_gain {
+struct __ec_align1 ec_param_ec_codec_get_shm_addr {
+ uint8_t shm_id;
+ uint8_t reserved[3];
+};
+
+struct __ec_align4 ec_param_ec_codec_set_shm_addr {
+ uint64_t phys_addr;
+ uint32_t len;
+ uint8_t shm_id;
+ uint8_t reserved[3];
+};
+
+struct __ec_align4 ec_param_ec_codec {
+ uint8_t cmd; /* enum ec_codec_subcmd */
+ uint8_t reserved[3];
+
+ union {
+ struct ec_param_ec_codec_get_shm_addr
+ get_shm_addr_param;
+ struct ec_param_ec_codec_set_shm_addr
+ set_shm_addr_param;
+ };
+};
+
+struct __ec_align4 ec_response_ec_codec_get_capabilities {
+ uint32_t capabilities;
+};
+
+struct __ec_align4 ec_response_ec_codec_get_shm_addr {
+ uint64_t phys_addr;
+ uint32_t len;
+ uint8_t type;
+ uint8_t reserved[3];
+};
+
+/*****************************************************************************/
+
+/* Commands for DMIC on audio codec. */
+#define EC_CMD_EC_CODEC_DMIC 0x00BD
+
+enum ec_codec_dmic_subcmd {
+ EC_CODEC_DMIC_GET_MAX_GAIN = 0x0,
+ EC_CODEC_DMIC_SET_GAIN_IDX = 0x1,
+ EC_CODEC_DMIC_GET_GAIN_IDX = 0x2,
+ EC_CODEC_DMIC_SUBCMD_COUNT,
+};
+
+enum ec_codec_dmic_channel {
+ EC_CODEC_DMIC_CHANNEL_0 = 0x0,
+ EC_CODEC_DMIC_CHANNEL_1 = 0x1,
+ EC_CODEC_DMIC_CHANNEL_2 = 0x2,
+ EC_CODEC_DMIC_CHANNEL_3 = 0x3,
+ EC_CODEC_DMIC_CHANNEL_4 = 0x4,
+ EC_CODEC_DMIC_CHANNEL_5 = 0x5,
+ EC_CODEC_DMIC_CHANNEL_6 = 0x6,
+ EC_CODEC_DMIC_CHANNEL_7 = 0x7,
+ EC_CODEC_DMIC_CHANNEL_COUNT,
+};
+
+struct __ec_align1 ec_param_ec_codec_dmic_set_gain_idx {
+ uint8_t channel; /* enum ec_codec_dmic_channel */
+ uint8_t gain;
+ uint8_t reserved[2];
+};
+
+struct __ec_align1 ec_param_ec_codec_dmic_get_gain_idx {
+ uint8_t channel; /* enum ec_codec_dmic_channel */
+ uint8_t reserved[3];
+};
+
+struct __ec_align4 ec_param_ec_codec_dmic {
+ uint8_t cmd; /* enum ec_codec_dmic_subcmd */
+ uint8_t reserved[3];
+
+ union {
+ struct ec_param_ec_codec_dmic_set_gain_idx
+ set_gain_idx_param;
+ struct ec_param_ec_codec_dmic_get_gain_idx
+ get_gain_idx_param;
+ };
+};
+
+struct __ec_align1 ec_response_ec_codec_dmic_get_max_gain {
+ uint8_t max_gain;
+};
+
+struct __ec_align1 ec_response_ec_codec_dmic_get_gain_idx {
+ uint8_t gain;
+};
+
+/*****************************************************************************/
+
+/* Commands for I2S RX on audio codec. */
+
+#define EC_CMD_EC_CODEC_I2S_RX 0x00BE
+
+enum ec_codec_i2s_rx_subcmd {
+ EC_CODEC_I2S_RX_ENABLE = 0x0,
+ EC_CODEC_I2S_RX_DISABLE = 0x1,
+ EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x2,
+ EC_CODEC_I2S_RX_SET_DAIFMT = 0x3,
+ EC_CODEC_I2S_RX_SET_BCLK = 0x4,
+ EC_CODEC_I2S_RX_SUBCMD_COUNT,
+};
+
+enum ec_codec_i2s_rx_sample_depth {
+ EC_CODEC_I2S_RX_SAMPLE_DEPTH_16 = 0x0,
+ EC_CODEC_I2S_RX_SAMPLE_DEPTH_24 = 0x1,
+ EC_CODEC_I2S_RX_SAMPLE_DEPTH_COUNT,
+};
+
+enum ec_codec_i2s_rx_daifmt {
+ EC_CODEC_I2S_RX_DAIFMT_I2S = 0x0,
+ EC_CODEC_I2S_RX_DAIFMT_RIGHT_J = 0x1,
+ EC_CODEC_I2S_RX_DAIFMT_LEFT_J = 0x2,
+ EC_CODEC_I2S_RX_DAIFMT_COUNT,
+};
+
+struct __ec_align1 ec_param_ec_codec_i2s_rx_set_sample_depth {
+ uint8_t depth;
+ uint8_t reserved[3];
+};
+
+struct __ec_align1 ec_param_ec_codec_i2s_rx_set_gain {
uint8_t left;
uint8_t right;
+ uint8_t reserved[2];
};
-struct __ec_todo_unpacked ec_param_codec_i2s_tdm {
- int16_t ch0_delay; /* 0 to 496 */
- int16_t ch1_delay; /* -1 to 496 */
- uint8_t adjacent_to_ch0;
- uint8_t adjacent_to_ch1;
+struct __ec_align1 ec_param_ec_codec_i2s_rx_set_daifmt {
+ uint8_t daifmt;
+ uint8_t reserved[3];
};
-struct __ec_todo_packed ec_param_codec_i2s {
- /* enum ec_codec_i2s_subcmd */
- uint8_t cmd;
+struct __ec_align4 ec_param_ec_codec_i2s_rx_set_bclk {
+ uint32_t bclk;
+};
+
+struct __ec_align4 ec_param_ec_codec_i2s_rx {
+ uint8_t cmd; /* enum ec_codec_i2s_rx_subcmd */
+ uint8_t reserved[3];
+
union {
- /*
- * EC_CODEC_SET_SAMPLE_DEPTH
- * Value should be one of ec_sample_depth_value.
- */
- uint8_t depth;
+ struct ec_param_ec_codec_i2s_rx_set_sample_depth
+ set_sample_depth_param;
+ struct ec_param_ec_codec_i2s_rx_set_daifmt
+ set_daifmt_param;
+ struct ec_param_ec_codec_i2s_rx_set_bclk
+ set_bclk_param;
+ };
+};
- /*
- * EC_CODEC_SET_GAIN
- * Value should be 0~43 for both channels.
- */
- struct ec_codec_i2s_gain gain;
+/*****************************************************************************/
+/* Commands for WoV on audio codec. */
+
+#define EC_CMD_EC_CODEC_WOV 0x00BF
+
+enum ec_codec_wov_subcmd {
+ EC_CODEC_WOV_SET_LANG = 0x0,
+ EC_CODEC_WOV_SET_LANG_SHM = 0x1,
+ EC_CODEC_WOV_GET_LANG = 0x2,
+ EC_CODEC_WOV_ENABLE = 0x3,
+ EC_CODEC_WOV_DISABLE = 0x4,
+ EC_CODEC_WOV_READ_AUDIO = 0x5,
+ EC_CODEC_WOV_READ_AUDIO_SHM = 0x6,
+ EC_CODEC_WOV_SUBCMD_COUNT,
+};
- /*
- * EC_CODEC_I2S_ENABLE
- * 1 to enable, 0 to disable.
- */
- uint8_t i2s_enable;
+/*
+ * @hash is SHA256 of the whole language model.
+ * @total_len indicates the length of whole language model.
+ * @offset is the cursor from the beginning of the model.
+ * @buf is the packet buffer.
+ * @len denotes how many bytes in the buf.
+ */
+struct __ec_align4 ec_param_ec_codec_wov_set_lang {
+ uint8_t hash[32];
+ uint32_t total_len;
+ uint32_t offset;
+ uint8_t buf[128];
+ uint32_t len;
+};
- /*
- * EC_CODEC_I2S_SET_CONFIG
- * Value should be one of ec_i2s_config.
- */
- uint8_t i2s_config;
+struct __ec_align4 ec_param_ec_codec_wov_set_lang_shm {
+ uint8_t hash[32];
+ uint32_t total_len;
+};
- /*
- * EC_CODEC_I2S_SET_TDM_CONFIG
- * Value should be one of ec_i2s_config.
- */
- struct ec_param_codec_i2s_tdm tdm_param;
+struct __ec_align4 ec_param_ec_codec_wov {
+ uint8_t cmd; /* enum ec_codec_wov_subcmd */
+ uint8_t reserved[3];
- /*
- * EC_CODEC_I2S_SET_BCLK
- */
- uint32_t bclk;
+ union {
+ struct ec_param_ec_codec_wov_set_lang
+ set_lang_param;
+ struct ec_param_ec_codec_wov_set_lang_shm
+ set_lang_shm_param;
};
};
+struct __ec_align4 ec_response_ec_codec_wov_get_lang {
+ uint8_t hash[32];
+};
+
+struct __ec_align4 ec_response_ec_codec_wov_read_audio {
+ uint8_t buf[128];
+ uint32_t len;
+};
+
+struct __ec_align4 ec_response_ec_codec_wov_read_audio_shm {
+ uint32_t offset;
+ uint32_t len;
+};
/*****************************************************************************/
/* System commands */
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index c679f6116580..b65220685920 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -83,6 +83,11 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
const struct snd_dmaengine_dai_dma_data *dma_data,
struct dma_slave_config *config);
+int snd_dmaengine_pcm_refine_runtime_hwparams(
+ struct snd_pcm_substream *substream,
+ struct snd_dmaengine_dai_dma_data *dma_data,
+ struct snd_pcm_hardware *hw,
+ struct dma_chan *chan);
/*
* Try to request the DMA channel using compat_request_channel or
diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h
index 9a0393cf024c..ac18f428eda6 100644
--- a/include/sound/hda_codec.h
+++ b/include/sound/hda_codec.h
@@ -254,6 +254,7 @@ struct hda_codec {
unsigned int force_pin_prefix:1; /* Add location prefix */
unsigned int link_down_at_suspend:1; /* link down at runtime suspend */
unsigned int relaxed_resume:1; /* don't resume forcibly for jack */
+ unsigned int mst_no_extra_pcms:1; /* no backup PCMs for DP-MST */
#ifdef CONFIG_PM
unsigned long power_on_acct;
diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h
index 6758fc12fa84..0feaf16e6ac0 100644
--- a/include/sound/pxa2xx-lib.h
+++ b/include/sound/pxa2xx-lib.h
@@ -10,6 +10,7 @@ struct snd_pcm_substream;
struct snd_pcm_hw_params;
struct snd_soc_pcm_runtime;
struct snd_pcm;
+struct snd_soc_component;
extern int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params);
@@ -23,8 +24,29 @@ extern int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma);
extern int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream);
extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm);
-extern int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd);
-extern const struct snd_pcm_ops pxa2xx_pcm_ops;
+extern void pxa2xx_soc_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm);
+extern int pxa2xx_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd);
+extern int pxa2xx_soc_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+extern int pxa2xx_soc_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+extern int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+extern int pxa2xx_soc_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+extern int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+extern int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd);
+extern snd_pcm_uframes_t
+pxa2xx_soc_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+extern int pxa2xx_soc_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma);
/* AC97 */
diff --git a/include/sound/rt5682.h b/include/sound/rt5682.h
index bf2ee75aabb1..bc2c31734df1 100644
--- a/include/sound/rt5682.h
+++ b/include/sound/rt5682.h
@@ -31,6 +31,7 @@ struct rt5682_platform_data {
enum rt5682_dmic1_data_pin dmic1_data_pin;
enum rt5682_dmic1_clk_pin dmic1_clk_pin;
enum rt5682_jd_src jd_src;
+ unsigned int btndet_delay;
};
#endif
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index 31f76b6abf71..bbdd1542d6f1 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -8,6 +8,7 @@
#ifndef __SIMPLE_CARD_UTILS_H
#define __SIMPLE_CARD_UTILS_H
+#include <linux/clk.h>
#include <sound/soc.h>
#define asoc_simple_init_hp(card, sjack, prefix) \
diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h
index 6c9929abd90b..20c0bee3b959 100644
--- a/include/sound/soc-acpi-intel-match.h
+++ b/include/sound/soc-acpi-intel-match.h
@@ -24,9 +24,12 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[];
/*
* generic table used for HDA codec-based platforms, possibly with
diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h
index 35b38e41e5b2..c4c997bd0379 100644
--- a/include/sound/soc-acpi.h
+++ b/include/sound/soc-acpi.h
@@ -60,12 +60,14 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg)
* @acpi_ipc_irq_index: used for BYT-CR detection
* @platform: string used for HDaudio codec support
* @codec_mask: used for HDAudio support
+ * @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver
*/
struct snd_soc_acpi_mach_params {
u32 acpi_ipc_irq_index;
const char *platform;
u32 codec_mask;
u32 dmic_num;
+ bool common_hdmi_codec_drv;
};
/**
@@ -75,6 +77,7 @@ struct snd_soc_acpi_mach_params {
* all firmware/topology related fields.
*
* @id: ACPI ID (usually the codec's) used to find a matching machine driver.
+ * @link_mask: describes required board layout, e.g. for SoundWire.
* @drv_name: machine driver name
* @fw_filename: firmware file name. Used when SOF is not enabled.
* @board: board name
@@ -90,6 +93,7 @@ struct snd_soc_acpi_mach_params {
/* Descriptor for SST ASoC machine driver */
struct snd_soc_acpi_mach {
const u8 id[ACPI_ID_LEN];
+ const u32 link_mask;
const char *drv_name;
const char *fw_filename;
const char *board;
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index 5d80b2eef525..f8fadf0e696a 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -3,10 +3,6 @@
* soc-component.h
*
* Copyright (c) 2019 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __SOC_COMPONENT_H
#define __SOC_COMPONENT_H
@@ -51,8 +47,10 @@ struct snd_soc_component_driver {
unsigned int reg, unsigned int val);
/* pcm creation and destruction */
- int (*pcm_new)(struct snd_soc_pcm_runtime *rtd);
- void (*pcm_free)(struct snd_pcm *pcm);
+ int (*pcm_construct)(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd);
+ void (*pcm_destruct)(struct snd_soc_component *component,
+ struct snd_pcm *pcm);
/* component wide operations */
int (*set_sysclk)(struct snd_soc_component *component,
@@ -74,7 +72,40 @@ struct snd_soc_component_driver {
int (*set_bias_level)(struct snd_soc_component *component,
enum snd_soc_bias_level level);
- const struct snd_pcm_ops *ops;
+ int (*open)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+ int (*close)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+ int (*ioctl)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg);
+ int (*hw_params)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+ int (*hw_free)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+ int (*prepare)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+ int (*trigger)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd);
+ snd_pcm_uframes_t (*pointer)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+ int (*get_time_info)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, struct timespec *system_ts,
+ struct timespec *audio_ts,
+ struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+ struct snd_pcm_audio_tstamp_report *audio_tstamp_report);
+ int (*copy_user)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int channel,
+ unsigned long pos, void __user *buf,
+ unsigned long bytes);
+ struct page *(*page)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned long offset);
+ int (*mmap)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma);
+
const struct snd_compr_ops *compr_ops;
/* probe ordering - for components with runtime dependencies */
@@ -381,7 +412,7 @@ struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream,
unsigned long offset);
int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma);
-int snd_soc_pcm_component_new(struct snd_pcm *pcm);
-void snd_soc_pcm_component_free(struct snd_pcm *pcm);
+int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd);
+void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd);
#endif /* __SOC_COMPONENT_H */
diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h
index e55aeb00ce2d..b654ebfc8766 100644
--- a/include/sound/soc-dpcm.h
+++ b/include/sound/soc-dpcm.h
@@ -103,15 +103,15 @@ struct snd_soc_dpcm_runtime {
int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
};
-#define for_each_dpcm_fe(be, stream, dpcm) \
- list_for_each_entry(dpcm, &(be)->dpcm[stream].fe_clients, list_fe)
-
-#define for_each_dpcm_be(fe, stream, dpcm) \
- list_for_each_entry(dpcm, &(fe)->dpcm[stream].be_clients, list_be)
-#define for_each_dpcm_be_safe(fe, stream, dpcm, _dpcm) \
- list_for_each_entry_safe(dpcm, _dpcm, &(fe)->dpcm[stream].be_clients, list_be)
-#define for_each_dpcm_be_rollback(fe, stream, dpcm) \
- list_for_each_entry_continue_reverse(dpcm, &(fe)->dpcm[stream].be_clients, list_be)
+#define for_each_dpcm_fe(be, stream, _dpcm) \
+ list_for_each_entry(_dpcm, &(be)->dpcm[stream].fe_clients, list_fe)
+
+#define for_each_dpcm_be(fe, stream, _dpcm) \
+ list_for_each_entry(_dpcm, &(fe)->dpcm[stream].be_clients, list_be)
+#define for_each_dpcm_be_safe(fe, stream, _dpcm, __dpcm) \
+ list_for_each_entry_safe(_dpcm, __dpcm, &(fe)->dpcm[stream].be_clients, list_be)
+#define for_each_dpcm_be_rollback(fe, stream, _dpcm) \
+ list_for_each_entry_continue_reverse(_dpcm, &(fe)->dpcm[stream].be_clients, list_be)
/* can this BE stop and free */
int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
diff --git a/include/sound/soc.h b/include/sound/soc.h
index f264c6509f00..c28a1ed5e8df 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -299,6 +299,12 @@
.put = snd_soc_bytes_put, .private_value = \
((unsigned long)&(struct soc_bytes) \
{.base = xbase, .num_regs = xregs }) }
+#define SND_SOC_BYTES_E(xname, xbase, xregs, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = xhandler_get, \
+ .put = xhandler_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) \
+ {.base = xbase, .num_regs = xregs }) }
#define SND_SOC_BYTES_MASK(xname, xbase, xregs, xmask) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@@ -739,10 +745,12 @@ struct snd_soc_rtdcom_list {
struct snd_soc_component*
snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
const char *driver_name);
-#define for_each_rtdcom(rtd, rtdcom) \
- list_for_each_entry(rtdcom, &(rtd)->component_list, list)
-#define for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) \
- list_for_each_entry_safe(rtdcom1, rtdcom2, &(rtd)->component_list, list)
+#define for_each_rtd_components(rtd, rtdcom, _component) \
+ for (rtdcom = list_first_entry(&(rtd)->component_list, \
+ typeof(*rtdcom), list); \
+ (&rtdcom->list != &(rtd)->component_list) && \
+ (_component = rtdcom->component); \
+ rtdcom = list_next_entry(rtdcom, list))
struct snd_soc_dai_link_component {
const char *name;
@@ -845,7 +853,9 @@ struct snd_soc_dai_link {
unsigned int ignore:1;
struct list_head list; /* DAI link list of the soc card */
+#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj; /* For topology */
+#endif
};
#define for_each_link_codecs(link, i, codec) \
for ((i) = 0; \
@@ -978,6 +988,7 @@ struct snd_soc_card {
const char *name;
const char *long_name;
const char *driver_name;
+ const char *components;
char dmi_longname[80];
char topology_shortname[32];
@@ -1148,7 +1159,6 @@ struct snd_soc_pcm_runtime {
struct list_head component_list; /* list of connected components */
/* bit field */
- unsigned int dev_registered:1;
unsigned int pop_wait:1;
unsigned int fe_compr:1; /* for Dynamic PCM */
};
@@ -1168,7 +1178,9 @@ struct soc_mixer_control {
unsigned int sign_bit;
unsigned int invert:1;
unsigned int autodisable:1;
+#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj;
+#endif
};
struct soc_bytes {
@@ -1179,8 +1191,9 @@ struct soc_bytes {
struct soc_bytes_ext {
int max;
+#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj;
-
+#endif
/* used for TLV byte control */
int (*get)(struct snd_kcontrol *kcontrol, unsigned int __user *bytes,
unsigned int size);
@@ -1204,7 +1217,9 @@ struct soc_enum {
const char * const *texts;
const unsigned int *values;
unsigned int autodisable:1;
+#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj;
+#endif
};
/* device driver data */
@@ -1325,8 +1340,10 @@ struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
int id, const char *name,
const char *stream_name);
-int snd_soc_register_dai(struct snd_soc_component *component,
- struct snd_soc_dai_driver *dai_drv);
+struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
+ struct snd_soc_dai_driver *dai_drv,
+ bool legacy_dai_naming);
+void snd_soc_unregister_dai(struct snd_soc_dai *dai);
struct snd_soc_dai *snd_soc_find_dai(
const struct snd_soc_dai_link_component *dlc);
@@ -1391,6 +1408,11 @@ static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm)
mutex_unlock(&dapm->card->dapm_mutex);
}
+/* bypass */
+int snd_soc_pcm_lib_ioctl(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg);
+
#include <sound/soc-component.h>
#endif
diff --git a/include/sound/sof.h b/include/sound/sof.h
index 4640566b54fe..479101736ee0 100644
--- a/include/sound/sof.h
+++ b/include/sound/sof.h
@@ -61,6 +61,9 @@ struct sof_dev_desc {
/* list of machines using this configuration */
struct snd_soc_acpi_mach *machines;
+ /* alternate list of machines using this configuration */
+ struct snd_soc_acpi_mach *alt_machines;
+
/* Platform resource indexes in BAR / ACPI resources. */
/* Must set to -1 if not used - add new items to end */
int resindex_lpe_base;
diff --git a/include/sound/sof/dai-imx.h b/include/sound/sof/dai-imx.h
new file mode 100644
index 000000000000..e02fb0b0fae1
--- /dev/null
+++ b/include/sound/sof/dai-imx.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright 2019 NXP
+ *
+ * Author: Daniel Baluta <daniel.baluta@nxp.com>
+ */
+
+#ifndef __INCLUDE_SOUND_SOF_DAI_IMX_H__
+#define __INCLUDE_SOUND_SOF_DAI_IMX_H__
+
+#include <sound/sof/header.h>
+
+/* ESAI Configuration Request - SOF_IPC_DAI_ESAI_CONFIG */
+struct sof_ipc_dai_esai_params {
+ struct sof_ipc_hdr hdr;
+
+ /* MCLK */
+ uint16_t reserved1;
+ uint16_t mclk_id;
+ uint32_t mclk_direction;
+
+ uint32_t mclk_rate; /* MCLK frequency in Hz */
+ uint32_t fsync_rate; /* FSYNC frequency in Hz */
+ uint32_t bclk_rate; /* BCLK frequency in Hz */
+
+ /* TDM */
+ uint32_t tdm_slots;
+ uint32_t rx_slots;
+ uint32_t tx_slots;
+ uint16_t tdm_slot_width;
+ uint16_t reserved2; /* alignment */
+} __packed;
+
+#endif
diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h
index 0f1235022146..c229565767e5 100644
--- a/include/sound/sof/dai.h
+++ b/include/sound/sof/dai.h
@@ -11,6 +11,7 @@
#include <sound/sof/header.h>
#include <sound/sof/dai-intel.h>
+#include <sound/sof/dai-imx.h>
/*
* DAI Configuration.
@@ -73,6 +74,7 @@ struct sof_ipc_dai_config {
struct sof_ipc_dai_dmic_params dmic;
struct sof_ipc_dai_hda_params hda;
struct sof_ipc_dai_alh_params alh;
+ struct sof_ipc_dai_esai_params esai;
};
} __packed;
diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h
index 10f00c08dbb7..bf3edd9c08b4 100644
--- a/include/sound/sof/header.h
+++ b/include/sound/sof/header.h
@@ -9,6 +9,7 @@
#ifndef __INCLUDE_SOUND_SOF_HEADER_H__
#define __INCLUDE_SOUND_SOF_HEADER_H__
+#include <linux/types.h>
#include <uapi/sound/sof/abi.h>
/** \addtogroup sof_uapi uAPI
@@ -74,6 +75,7 @@
#define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005)
#define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006)
#define SOF_IPC_PM_CORE_ENABLE SOF_CMD_TYPE(0x007)
+#define SOF_IPC_PM_GATE SOF_CMD_TYPE(0x008)
/* component runtime config - multiple different types */
#define SOF_IPC_COMP_SET_VALUE SOF_CMD_TYPE(0x001)
diff --git a/include/sound/sof/pm.h b/include/sound/sof/pm.h
index 003879401d63..3cf2e0f39d94 100644
--- a/include/sound/sof/pm.h
+++ b/include/sound/sof/pm.h
@@ -45,4 +45,12 @@ struct sof_ipc_pm_core_config {
uint32_t enable_mask;
} __packed;
+struct sof_ipc_pm_gate {
+ struct sof_ipc_cmd_hdr hdr;
+ uint32_t flags; /* platform specific */
+
+ /* reserved for future use */
+ uint32_t reserved[5];
+} __packed;
+
#endif
diff --git a/include/sound/sof/stream.h b/include/sound/sof/stream.h
index 0b71b381b952..7facefb541b3 100644
--- a/include/sound/sof/stream.h
+++ b/include/sound/sof/stream.h
@@ -83,10 +83,10 @@ struct sof_ipc_stream_params {
uint16_t sample_valid_bytes;
uint16_t sample_container_bytes;
- /* for notifying host period has completed - 0 means no period IRQ */
uint32_t host_period_bytes;
+ uint16_t no_stream_position; /**< 1 means don't send stream position */
- uint32_t reserved[2];
+ uint16_t reserved[3];
uint16_t chmap[SOF_IPC_MAX_CHANNELS]; /**< channel map - SOF_CHMAP_ */
} __packed;
diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h
index 14074405f501..88ac1870510e 100644
--- a/include/sound/wm8904.h
+++ b/include/sound/wm8904.h
@@ -120,7 +120,7 @@
* DRC configurations are specified with a label and a set of register
* values to write (the enable bits will be ignored). At runtime an
* enumerated control will be presented for each DRC block allowing
- * the user to choose the configration to use.
+ * the user to choose the configuration to use.
*
* Configurations may be generated by hand or by using the DRC control
* panel provided by the WISCE - see http://www.wolfsonmicro.com/wisce/
diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h
index 3d4d6de66a17..9c96fb0e4d90 100644
--- a/include/uapi/sound/compress_params.h
+++ b/include/uapi/sound/compress_params.h
@@ -317,12 +317,22 @@ struct snd_enc_generic {
__s32 reserved[15]; /* Can be used for SND_AUDIOCODEC_BESPOKE */
} __attribute__((packed, aligned(4)));
+struct snd_dec_flac {
+ __u16 sample_size;
+ __u16 min_blk_size;
+ __u16 max_blk_size;
+ __u16 min_frame_size;
+ __u16 max_frame_size;
+ __u16 reserved;
+} __attribute__((packed, aligned(4)));
+
union snd_codec_options {
struct snd_enc_wma wma;
struct snd_enc_vorbis vorbis;
struct snd_enc_real real;
struct snd_enc_flac flac;
struct snd_enc_generic generic;
+ struct snd_dec_flac flac_d;
} __attribute__((packed, aligned(4)));
/** struct snd_codec_desc - description of codec capabilities
diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h
index a0fe0d4c4b66..ebfdc20ca081 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 10
+#define SOF_ABI_MINOR 11
#define SOF_ABI_PATCH 0
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h
index 8f996857fb24..76883e6fb750 100644
--- a/include/uapi/sound/sof/tokens.h
+++ b/include/uapi/sound/sof/tokens.h
@@ -111,7 +111,14 @@
/* TODO: Add SAI tokens */
/* ESAI */
-#define SOF_TKN_IMX_ESAI_FIRST_TOKEN 1100
-/* TODO: Add ESAI tokens */
+#define SOF_TKN_IMX_ESAI_MCLK_ID 1100
+
+/* Stream */
+#define SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3 1200
+#define SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3 1201
+
+/* Led control for mute switches */
+#define SOF_TKN_MUTE_LED_USE 1300
+#define SOF_TKN_MUTE_LED_DIRECTION 1301
#endif
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index 54500bd098f9..a86c95d89824 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -175,7 +175,15 @@ void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers);
-int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+void pxa2xx_soc_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
+{
+ pxa2xx_pcm_free_dma_buffers(pcm);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_free);
+
+int pxa2xx_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -203,18 +211,64 @@ int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
}
EXPORT_SYMBOL(pxa2xx_soc_pcm_new);
-const struct snd_pcm_ops pxa2xx_pcm_ops = {
- .open = pxa2xx_pcm_open,
- .close = pxa2xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pxa2xx_pcm_hw_params,
- .hw_free = pxa2xx_pcm_hw_free,
- .prepare = pxa2xx_pcm_prepare,
- .trigger = pxa2xx_pcm_trigger,
- .pointer = pxa2xx_pcm_pointer,
- .mmap = pxa2xx_pcm_mmap,
-};
-EXPORT_SYMBOL(pxa2xx_pcm_ops);
+int pxa2xx_soc_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_open(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_open);
+
+int pxa2xx_soc_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_close(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_close);
+
+int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return pxa2xx_pcm_hw_params(substream, params);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_params);
+
+int pxa2xx_soc_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_hw_free(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_free);
+
+int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_prepare(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_prepare);
+
+int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ return pxa2xx_pcm_trigger(substream, cmd);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_trigger);
+
+snd_pcm_uframes_t
+pxa2xx_soc_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_pointer(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_pointer);
+
+int pxa2xx_soc_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return pxa2xx_pcm_mmap(substream, vma);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_mmap);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("Intel PXA2xx sound library");
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index 89a05926ac73..5749a8a49784 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -369,4 +369,87 @@ int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
+/**
+ * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params
+ * @substream: PCM substream
+ * @dma_data: DAI DMA data
+ * @hw: PCM hw params
+ * @chan: DMA channel to use for data transfers
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ *
+ * This function will query DMA capability, then refine the pcm hardware
+ * parameters.
+ */
+int snd_dmaengine_pcm_refine_runtime_hwparams(
+ struct snd_pcm_substream *substream,
+ struct snd_dmaengine_dai_dma_data *dma_data,
+ struct snd_pcm_hardware *hw,
+ struct dma_chan *chan)
+{
+ struct dma_slave_caps dma_caps;
+ u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ snd_pcm_format_t i;
+ int ret = 0;
+
+ if (!hw || !chan || !dma_data)
+ return -EINVAL;
+
+ ret = dma_get_slave_caps(chan, &dma_caps);
+ if (ret == 0) {
+ if (dma_caps.cmd_pause && dma_caps.cmd_resume)
+ hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
+ if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
+ hw->info |= SNDRV_PCM_INFO_BATCH;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ addr_widths = dma_caps.dst_addr_widths;
+ else
+ addr_widths = dma_caps.src_addr_widths;
+ }
+
+ /*
+ * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
+ * hw.formats set to 0, meaning no restrictions are in place.
+ * In this case it's the responsibility of the DAI driver to
+ * provide the supported format information.
+ */
+ if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
+ /*
+ * Prepare formats mask for valid/allowed sample types. If the
+ * dma does not have support for the given physical word size,
+ * it needs to be masked out so user space can not use the
+ * format which produces corrupted audio.
+ * In case the dma driver does not implement the slave_caps the
+ * default assumption is that it supports 1, 2 and 4 bytes
+ * widths.
+ */
+ for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) {
+ int bits = snd_pcm_format_physical_width(i);
+
+ /*
+ * Enable only samples with DMA supported physical
+ * widths
+ */
+ switch (bits) {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ case 64:
+ if (addr_widths & (1 << (bits / 8)))
+ hw->formats |= pcm_format_to_bits(i);
+ break;
+ default:
+ /* Unsupported types */
+ break;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams);
+
MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 78bd2e3722c7..d24bd95c6e95 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -2075,15 +2075,24 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
static int generic_hdmi_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int idx;
+ int idx, pcm_num;
/*
* for non-mst mode, pcm number is the same as before
- * for DP MST mode, pcm number is (nid number + dev_num - 1)
- * dev_num is the device entry number in a pin
- *
+ * for DP MST mode without extra PCM, pcm number is same
+ * for DP MST mode with extra PCMs, pcm number is
+ * (nid number + dev_num - 1)
+ * dev_num is the device entry number in a pin
*/
- for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) {
+
+ if (codec->mst_no_extra_pcms)
+ pcm_num = spec->num_nids;
+ else
+ pcm_num = spec->num_nids + spec->dev_num - 1;
+
+ codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
+
+ for (idx = 0; idx < pcm_num; idx++) {
struct hda_pcm *info;
struct hda_pcm_stream *pstr;
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
index 52225b4b6382..4b9a27e25206 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -759,14 +759,12 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
return IRQ_NONE;
}
-static int acp_dma_open(struct snd_pcm_substream *substream)
+static int acp_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
u16 bank;
int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
- DRV_NAME);
struct audio_drv_data *intr_data = dev_get_drvdata(component->dev);
struct audio_substream_data *adata =
kzalloc(sizeof(struct audio_substream_data), GFP_KERNEL);
@@ -834,7 +832,8 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
return 0;
}
-static int acp_dma_hw_params(struct snd_pcm_substream *substream,
+static int acp_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int status;
@@ -843,8 +842,6 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime;
struct audio_substream_data *rtd;
struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
- DRV_NAME);
struct audio_drv_data *adata = dev_get_drvdata(component->dev);
struct snd_soc_card *card = prtd->card;
struct acp_platform_info *pinfo = snd_soc_card_get_drvdata(card);
@@ -995,7 +992,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
return status;
}
-static int acp_dma_hw_free(struct snd_pcm_substream *substream)
+static int acp_dma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
@@ -1011,7 +1009,8 @@ static u64 acp_get_byte_count(struct audio_substream_data *rtd)
return byte_count.bytescount;
}
-static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
u32 buffersize;
u32 pos = 0;
@@ -1053,13 +1052,15 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, pos);
}
-static int acp_dma_mmap(struct snd_pcm_substream *substream,
+static int acp_dma_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
return snd_pcm_lib_default_mmap(substream, vma);
}
-static int acp_dma_prepare(struct snd_pcm_substream *substream)
+static int acp_dma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data;
@@ -1086,7 +1087,8 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+static int acp_dma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
int ret;
@@ -1132,10 +1134,9 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
-static int acp_dma_new(struct snd_soc_pcm_runtime *rtd)
+static int acp_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd,
- DRV_NAME);
struct audio_drv_data *adata = dev_get_drvdata(component->dev);
struct device *parent = component->dev->parent;
@@ -1158,14 +1159,12 @@ static int acp_dma_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static int acp_dma_close(struct snd_pcm_substream *substream)
+static int acp_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
u16 bank;
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
- DRV_NAME);
struct audio_drv_data *adata = dev_get_drvdata(component->dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -1216,22 +1215,18 @@ static int acp_dma_close(struct snd_pcm_substream *substream)
return 0;
}
-static const struct snd_pcm_ops acp_dma_ops = {
- .open = acp_dma_open,
- .close = acp_dma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = acp_dma_hw_params,
- .hw_free = acp_dma_hw_free,
- .trigger = acp_dma_trigger,
- .pointer = acp_dma_pointer,
- .mmap = acp_dma_mmap,
- .prepare = acp_dma_prepare,
-};
-
static const struct snd_soc_component_driver acp_asoc_platform = {
- .name = DRV_NAME,
- .ops = &acp_dma_ops,
- .pcm_new = acp_dma_new,
+ .name = DRV_NAME,
+ .open = acp_dma_open,
+ .close = acp_dma_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = acp_dma_hw_params,
+ .hw_free = acp_dma_hw_free,
+ .trigger = acp_dma_trigger,
+ .pointer = acp_dma_pointer,
+ .mmap = acp_dma_mmap,
+ .prepare = acp_dma_prepare,
+ .pcm_construct = acp_dma_new,
};
static int acp_audio_probe(struct platform_device *pdev)
diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c
index bc4dfafdfcd1..60709e3ba99d 100644
--- a/sound/soc/amd/raven/acp3x-pcm-dma.c
+++ b/sound/soc/amd/raven/acp3x-pcm-dma.c
@@ -275,16 +275,12 @@ static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction)
rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL);
}
-static int acp3x_dma_open(struct snd_pcm_substream *substream)
+static int acp3x_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
int ret = 0;
-
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
- DRV_NAME);
struct i2s_dev_data *adata = dev_get_drvdata(component->dev);
-
struct i2s_stream_instance *i2s_data = kzalloc(sizeof(struct i2s_stream_instance),
GFP_KERNEL);
if (!i2s_data)
@@ -334,7 +330,8 @@ static u64 acp_get_byte_count(struct i2s_stream_instance *rtd, int direction)
return byte_count;
}
-static int acp3x_dma_hw_params(struct snd_pcm_substream *substream,
+static int acp3x_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int status;
@@ -362,7 +359,8 @@ static int acp3x_dma_hw_params(struct snd_pcm_substream *substream,
return status;
}
-static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
u32 pos = 0;
u32 buffersize = 0;
@@ -379,33 +377,32 @@ static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(substream->runtime, pos);
}
-static int acp3x_dma_new(struct snd_soc_pcm_runtime *rtd)
+static int acp3x_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd,
- DRV_NAME);
struct device *parent = component->dev->parent;
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
parent, MIN_BUFFER, MAX_BUFFER);
return 0;
}
-static int acp3x_dma_hw_free(struct snd_pcm_substream *substream)
+static int acp3x_dma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
-static int acp3x_dma_mmap(struct snd_pcm_substream *substream,
+static int acp3x_dma_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
return snd_pcm_lib_default_mmap(substream, vma);
}
-static int acp3x_dma_close(struct snd_pcm_substream *substream)
+static int acp3x_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
struct i2s_stream_instance *rtd = substream->runtime->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
- DRV_NAME);
struct i2s_dev_data *adata = dev_get_drvdata(component->dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -422,17 +419,6 @@ static int acp3x_dma_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops acp3x_dma_ops = {
- .open = acp3x_dma_open,
- .close = acp3x_dma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = acp3x_dma_hw_params,
- .hw_free = acp3x_dma_hw_free,
- .pointer = acp3x_dma_pointer,
- .mmap = acp3x_dma_mmap,
-};
-
-
static int acp3x_dai_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
@@ -610,9 +596,15 @@ static struct snd_soc_dai_driver acp3x_i2s_dai_driver = {
};
static const struct snd_soc_component_driver acp3x_i2s_component = {
- .name = DRV_NAME,
- .ops = &acp3x_dma_ops,
- .pcm_new = acp3x_dma_new,
+ .name = DRV_NAME,
+ .open = acp3x_dma_open,
+ .close = acp3x_dma_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = acp3x_dma_hw_params,
+ .hw_free = acp3x_dma_hw_free,
+ .pointer = acp3x_dma_pointer,
+ .mmap = acp3x_dma_mmap,
+ .pcm_construct = acp3x_dma_new,
};
static int acp3x_audio_probe(struct platform_device *pdev)
@@ -631,7 +623,7 @@ static int acp3x_audio_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
- return -ENODEV;
+ return -ENODEV;
}
adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c
index ed095af866db..18a2fd02fffe 100644
--- a/sound/soc/atmel/atmel-pcm-pdc.c
+++ b/sound/soc/atmel/atmel-pcm-pdc.c
@@ -56,15 +56,17 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
return 0;
}
-static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static int atmel_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
{
return remap_pfn_range(vma, vma->vm_start,
substream->dma_buffer.addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
-static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int atmel_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -93,7 +95,8 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
return ret;
}
-static void atmel_pcm_free(struct snd_pcm *pcm)
+static void atmel_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
@@ -196,8 +199,9 @@ static void atmel_pcm_dma_irq(u32 ssc_sr,
/*--------------------------------------------------------------------------*\
* PCM operations
\*--------------------------------------------------------------------------*/
-static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int atmel_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct atmel_runtime_data *prtd = runtime->private_data;
@@ -225,7 +229,8 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
+static int atmel_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct atmel_runtime_data *prtd = substream->runtime->private_data;
struct atmel_pcm_dma_params *params = prtd->params;
@@ -239,7 +244,8 @@ static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
+static int atmel_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct atmel_runtime_data *prtd = substream->runtime->private_data;
struct atmel_pcm_dma_params *params = prtd->params;
@@ -251,8 +257,8 @@ static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
+static int atmel_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *rtd = substream->runtime;
struct atmel_runtime_data *prtd = rtd->private_data;
@@ -317,8 +323,8 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
return ret;
}
-static snd_pcm_uframes_t atmel_pcm_pointer(
- struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct atmel_runtime_data *prtd = runtime->private_data;
@@ -335,7 +341,8 @@ static snd_pcm_uframes_t atmel_pcm_pointer(
return x;
}
-static int atmel_pcm_open(struct snd_pcm_substream *substream)
+static int atmel_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct atmel_runtime_data *prtd;
@@ -360,7 +367,8 @@ static int atmel_pcm_open(struct snd_pcm_substream *substream)
return ret;
}
-static int atmel_pcm_close(struct snd_pcm_substream *substream)
+static int atmel_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct atmel_runtime_data *prtd = substream->runtime->private_data;
@@ -368,22 +376,18 @@ static int atmel_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static const struct snd_pcm_ops atmel_pcm_ops = {
+static const struct snd_soc_component_driver atmel_soc_platform = {
.open = atmel_pcm_open,
.close = atmel_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
+ .ioctl = snd_soc_pcm_lib_ioctl,
.hw_params = atmel_pcm_hw_params,
.hw_free = atmel_pcm_hw_free,
.prepare = atmel_pcm_prepare,
.trigger = atmel_pcm_trigger,
.pointer = atmel_pcm_pointer,
.mmap = atmel_pcm_mmap,
-};
-
-static struct snd_soc_component_driver atmel_soc_platform = {
- .ops = &atmel_pcm_ops,
- .pcm_new = atmel_pcm_new,
- .pcm_free = atmel_pcm_free,
+ .pcm_construct = atmel_pcm_new,
+ .pcm_destruct = atmel_pcm_free,
};
int atmel_pcm_pdc_platform_register(struct device *dev)
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index d56092a5ee11..4553108ec92a 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -182,15 +182,15 @@ out:
return 0;
}
-static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss)
+static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
{
- struct snd_soc_pcm_runtime *rtd = ss->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct au1xpsc_audio_dmadata *pcd = snd_soc_component_get_drvdata(component);
return &pcd[ss->stream];
}
-static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
+static int au1xpsc_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -202,7 +202,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
goto out;
stype = substream->stream;
- pcd = to_dmadata(substream);
+ pcd = to_dmadata(substream, component);
DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
"runtime->min_align %lu\n",
@@ -232,15 +232,17 @@ out:
return ret;
}
-static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_pcm_lib_free_pages(substream);
return 0;
}
-static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
+ struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
au1xxx_dbdma_reset(pcd->ddma_chan);
@@ -255,9 +257,10 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int au1xpsc_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
- u32 c = to_dmadata(substream)->ddma_chan;
+ u32 c = to_dmadata(substream, component)->ddma_chan;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -275,14 +278,17 @@ static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
static snd_pcm_uframes_t
-au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
+au1xpsc_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- return bytes_to_frames(substream->runtime, to_dmadata(substream)->pos);
+ return bytes_to_frames(substream->runtime,
+ to_dmadata(substream, component)->pos);
}
-static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
+ struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int stype = substream->stream, *dmaids;
@@ -296,24 +302,15 @@ static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- au1x_pcm_dbdma_free(to_dmadata(substream));
+ au1x_pcm_dbdma_free(to_dmadata(substream, component));
return 0;
}
-static const struct snd_pcm_ops au1xpsc_pcm_ops = {
- .open = au1xpsc_pcm_open,
- .close = au1xpsc_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = au1xpsc_pcm_hw_params,
- .hw_free = au1xpsc_pcm_hw_free,
- .prepare = au1xpsc_pcm_prepare,
- .trigger = au1xpsc_pcm_trigger,
- .pointer = au1xpsc_pcm_pointer,
-};
-
-static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int au1xpsc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -327,8 +324,15 @@ static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
/* au1xpsc audio platform */
static struct snd_soc_component_driver au1xpsc_soc_component = {
.name = DRV_NAME,
- .ops = &au1xpsc_pcm_ops,
- .pcm_new = au1xpsc_pcm_new,
+ .open = au1xpsc_pcm_open,
+ .close = au1xpsc_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = au1xpsc_pcm_hw_params,
+ .hw_free = au1xpsc_pcm_hw_free,
+ .prepare = au1xpsc_pcm_prepare,
+ .trigger = au1xpsc_pcm_trigger,
+ .pointer = au1xpsc_pcm_pointer,
+ .pcm_construct = au1xpsc_pcm_new,
};
static int au1xpsc_pcm_drvprobe(struct platform_device *pdev)
diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c
index 1e98cc4f9e27..49556eb409cd 100644
--- a/sound/soc/au1x/dma.c
+++ b/sound/soc/au1x/dma.c
@@ -174,22 +174,23 @@ static const struct snd_pcm_hardware alchemy_pcm_hardware = {
.fifo_size = 16,
};
-static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss)
+static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
{
- struct snd_soc_pcm_runtime *rtd = ss->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
return snd_soc_component_get_drvdata(component);
}
-static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss)
+static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
{
- struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss);
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss, component);
return &(ctx->stream[ss->stream]);
}
-static int alchemy_pcm_open(struct snd_pcm_substream *substream)
+static int alchemy_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int *dmaids, s = substream->stream;
char *name;
@@ -213,9 +214,10 @@ static int alchemy_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int alchemy_pcm_close(struct snd_pcm_substream *substream)
+static int alchemy_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
int stype = substream->stream;
ctx->stream[stype].substream = NULL;
@@ -224,10 +226,11 @@ static int alchemy_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
+static int alchemy_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- struct audio_stream *stream = ss_to_as(substream);
+ struct audio_stream *stream = ss_to_as(substream, component);
int err;
err = snd_pcm_lib_malloc_pages(substream,
@@ -243,16 +246,18 @@ static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
return err;
}
-static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream)
+static int alchemy_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct audio_stream *stream = ss_to_as(substream);
+ struct audio_stream *stream = ss_to_as(substream, component);
au1000_release_dma_link(stream);
return snd_pcm_lib_free_pages(substream);
}
-static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int alchemy_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
- struct audio_stream *stream = ss_to_as(substream);
+ struct audio_stream *stream = ss_to_as(substream, component);
int err = 0;
switch (cmd) {
@@ -269,9 +274,10 @@ static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return err;
}
-static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
+static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
- struct audio_stream *stream = ss_to_as(ss);
+ struct audio_stream *stream = ss_to_as(ss, component);
long location;
location = get_dma_residue(stream->dma);
@@ -281,17 +287,8 @@ static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, location);
}
-static const struct snd_pcm_ops alchemy_pcm_ops = {
- .open = alchemy_pcm_open,
- .close = alchemy_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = alchemy_pcm_hw_params,
- .hw_free = alchemy_pcm_hw_free,
- .trigger = alchemy_pcm_trigger,
- .pointer = alchemy_pcm_pointer,
-};
-
-static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int alchemy_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
@@ -303,8 +300,14 @@ static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
static struct snd_soc_component_driver alchemy_pcm_soc_component = {
.name = DRV_NAME,
- .ops = &alchemy_pcm_ops,
- .pcm_new = alchemy_pcm_new,
+ .open = alchemy_pcm_open,
+ .close = alchemy_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = alchemy_pcm_hw_params,
+ .hw_free = alchemy_pcm_hw_free,
+ .trigger = alchemy_pcm_trigger,
+ .pointer = alchemy_pcm_pointer,
+ .pcm_construct = alchemy_pcm_new,
};
static int alchemy_pcm_drvprobe(struct platform_device *pdev)
diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c
index 8966b02844dc..c65408085c1d 100644
--- a/sound/soc/bcm/cygnus-pcm.c
+++ b/sound/soc/bcm/cygnus-pcm.c
@@ -376,7 +376,8 @@ static void disable_intr(struct snd_pcm_substream *substream)
}
-static int cygnus_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int cygnus_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
@@ -577,7 +578,8 @@ static irqreturn_t cygnus_dma_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int cygnus_pcm_open(struct snd_pcm_substream *substream)
+static int cygnus_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -613,7 +615,8 @@ static int cygnus_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int cygnus_pcm_close(struct snd_pcm_substream *substream)
+static int cygnus_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct cygnus_aio_port *aio;
@@ -633,8 +636,9 @@ static int cygnus_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int cygnus_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -649,7 +653,8 @@ static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
+static int cygnus_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct cygnus_aio_port *aio;
@@ -661,7 +666,8 @@ static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
+static int cygnus_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -694,7 +700,8 @@ static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct cygnus_aio_port *aio;
unsigned int res = 0, cur = 0, base = 0;
@@ -750,19 +757,8 @@ static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
return 0;
}
-
-static const struct snd_pcm_ops cygnus_pcm_ops = {
- .open = cygnus_pcm_open,
- .close = cygnus_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = cygnus_pcm_hw_params,
- .hw_free = cygnus_pcm_hw_free,
- .prepare = cygnus_pcm_prepare,
- .trigger = cygnus_pcm_trigger,
- .pointer = cygnus_pcm_pointer,
-};
-
-static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
+static void cygnus_dma_free_dma_buffers(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
@@ -788,7 +784,8 @@ static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
}
}
-static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
+static int cygnus_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -810,7 +807,7 @@ static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
ret = cygnus_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret) {
- cygnus_dma_free_dma_buffers(pcm);
+ cygnus_dma_free_dma_buffers(component, pcm);
return ret;
}
}
@@ -819,9 +816,16 @@ static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
}
static struct snd_soc_component_driver cygnus_soc_platform = {
- .ops = &cygnus_pcm_ops,
- .pcm_new = cygnus_dma_new,
- .pcm_free = cygnus_dma_free_dma_buffers,
+ .open = cygnus_pcm_open,
+ .close = cygnus_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = cygnus_pcm_hw_params,
+ .hw_free = cygnus_pcm_hw_free,
+ .prepare = cygnus_pcm_prepare,
+ .trigger = cygnus_pcm_trigger,
+ .pointer = cygnus_pcm_pointer,
+ .pcm_construct = cygnus_dma_new,
+ .pcm_destruct = cygnus_dma_free_dma_buffers,
};
int cygnus_soc_platform_register(struct device *dev,
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 229cc89f8c5a..ec01e4f12a78 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -34,6 +34,8 @@ config SND_SOC_ALL_CODECS
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
@@ -179,6 +181,8 @@ config SND_SOC_ALL_CODECS
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
@@ -395,6 +399,33 @@ config SND_SOC_ADAU1977_I2C
config SND_SOC_ADAU7002
tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter"
+config SND_SOC_ADAU7118
+ tristate
+
+config SND_SOC_ADAU7118_HW
+ tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - HW Mode"
+ select SND_SOC_ADAU7118
+ help
+ Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
+ Converter. In this mode, the device works in standalone mode which
+ means that there is no bus to comunicate with it. Stereo mode is not
+ supported in this mode.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-adau7118-hw.
+
+config SND_SOC_ADAU7118_I2C
+ tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - I2C"
+ depends on I2C
+ select SND_SOC_ADAU7118
+ select REGMAP_I2C
+ help
+ Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
+ Converter over I2C. This gives full support over the device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-adau7118-i2c.
+
config SND_SOC_ADAV80X
tristate
@@ -478,6 +509,8 @@ config SND_SOC_CQ0093VC
config SND_SOC_CROS_EC_CODEC
tristate "codec driver for ChromeOS EC"
depends on CROS_EC
+ select CRYPTO
+ select CRYPTO_SHA256
help
If you say yes here you will get support for the
ChromeOS Embedded Controller's Audio Codec.
@@ -646,7 +679,8 @@ config SND_SOC_DA7210
tristate
config SND_SOC_DA7213
- tristate
+ tristate "Dialog DA7213 CODEC"
+ depends on I2C
config SND_SOC_DA7218
tristate
@@ -1104,6 +1138,14 @@ config SND_SOC_TAS2552
tristate "Texas Instruments TAS2552 Mono Audio amplifier"
depends on I2C
+config SND_SOC_TAS2562
+ tristate "Texas Instruments TAS2562 Mono Audio amplifier"
+ depends on I2C
+
+config SND_SOC_TAS2770
+ tristate "Texas Instruments TAS2770 speaker amplifier"
+ depends on I2C
+
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index c498373dcc5f..ddfd07071925 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -22,6 +22,9 @@ snd-soc-adau1977-objs := adau1977.o
snd-soc-adau1977-spi-objs := adau1977-spi.o
snd-soc-adau1977-i2c-objs := adau1977-i2c.o
snd-soc-adau7002-objs := adau7002.o
+snd-soc-adau7118-objs := adau7118.o
+snd-soc-adau7118-i2c-objs := adau7118-i2c.o
+snd-soc-adau7118-hw-objs := adau7118-hw.o
snd-soc-adav80x-objs := adav80x.o
snd-soc-adav801-objs := adav801.o
snd-soc-adav803-objs := adav803.o
@@ -196,6 +199,7 @@ snd-soc-tas571x-objs := tas571x.o
snd-soc-tas5720-objs := tas5720.o
snd-soc-tas6424-objs := tas6424.o
snd-soc-tda7419-objs := tda7419.o
+snd-soc-tas2770-objs := tas2770.o
snd-soc-tfa9879-objs := tfa9879.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -280,6 +284,7 @@ snd-soc-max98504-objs := max98504.o
snd-soc-simple-amplifier-objs := simple-amplifier.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-tas2552-objs := tas2552.o
+snd-soc-tas2562-objs := tas2562.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
@@ -304,6 +309,9 @@ obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o
obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o
obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o
+obj-$(CONFIG_SND_SOC_ADAU7118) += snd-soc-adau7118.o
+obj-$(CONFIG_SND_SOC_ADAU7118_I2C) += snd-soc-adau7118-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU7118_HW) += snd-soc-adau7118-hw.o
obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o
obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
@@ -474,11 +482,13 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
+obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o
obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o
+obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index 977f5a63be3f..5ca9b744b7d8 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -28,6 +28,10 @@
#define ADAU1761_REC_MIXER_RIGHT1 0x400d
#define ADAU1761_LEFT_DIFF_INPUT_VOL 0x400e
#define ADAU1761_RIGHT_DIFF_INPUT_VOL 0x400f
+#define ADAU1761_ALC_CTRL0 0x4011
+#define ADAU1761_ALC_CTRL1 0x4012
+#define ADAU1761_ALC_CTRL2 0x4013
+#define ADAU1761_ALC_CTRL3 0x4014
#define ADAU1761_PLAY_LR_MIXER_LEFT 0x4020
#define ADAU1761_PLAY_MIXER_LEFT0 0x401c
#define ADAU1761_PLAY_MIXER_LEFT1 0x401d
@@ -71,6 +75,10 @@ static const struct reg_default adau1761_reg_defaults[] = {
{ ADAU1761_REC_MIXER_RIGHT0, 0x00 },
{ ADAU1761_REC_MIXER_RIGHT1, 0x00 },
{ ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 },
+ { ADAU1761_ALC_CTRL0, 0x00 },
+ { ADAU1761_ALC_CTRL1, 0x00 },
+ { ADAU1761_ALC_CTRL2, 0x00 },
+ { ADAU1761_ALC_CTRL3, 0x00 },
{ ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 },
{ ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 },
{ ADAU1761_PLAY_MIXER_LEFT0, 0x00 },
@@ -121,6 +129,10 @@ static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_alc_max_gain_tlv, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_alc_target_tlv, -2850, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_alc_ng_threshold_tlv, -7650, 150, 0);
+
static const unsigned int adau1761_bias_select_values[] = {
0, 2, 3,
};
@@ -147,6 +159,103 @@ static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum,
ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text,
adau1761_bias_select_values);
+static const unsigned int adau1761_pga_slew_time_values[] = {
+ 3, 0, 1, 2,
+};
+
+static const char * const adau1761_pga_slew_time_text[] = {
+ "Off",
+ "24 ms",
+ "48 ms",
+ "96 ms",
+};
+
+static const char * const adau1761_alc_function_text[] = {
+ "Off",
+ "Right",
+ "Left",
+ "Stereo",
+ "DSP control",
+};
+
+static const char * const adau1761_alc_hold_time_text[] = {
+ "2.67 ms",
+ "5.34 ms",
+ "10.68 ms",
+ "21.36 ms",
+ "42.72 ms",
+ "85.44 ms",
+ "170.88 ms",
+ "341.76 ms",
+ "683.52 ms",
+ "1367 ms",
+ "2734.1 ms",
+ "5468.2 ms",
+ "10936 ms",
+ "21873 ms",
+ "43745 ms",
+ "87491 ms",
+};
+
+static const char * const adau1761_alc_attack_time_text[] = {
+ "6 ms",
+ "12 ms",
+ "24 ms",
+ "48 ms",
+ "96 ms",
+ "192 ms",
+ "384 ms",
+ "768 ms",
+ "1540 ms",
+ "3070 ms",
+ "6140 ms",
+ "12290 ms",
+ "24580 ms",
+ "49150 ms",
+ "98300 ms",
+ "196610 ms",
+};
+
+static const char * const adau1761_alc_decay_time_text[] = {
+ "24 ms",
+ "48 ms",
+ "96 ms",
+ "192 ms",
+ "384 ms",
+ "768 ms",
+ "15400 ms",
+ "30700 ms",
+ "61400 ms",
+ "12290 ms",
+ "24580 ms",
+ "49150 ms",
+ "98300 ms",
+ "196610 ms",
+ "393220 ms",
+ "786430 ms",
+};
+
+static const char * const adau1761_alc_ng_type_text[] = {
+ "Hold",
+ "Mute",
+ "Fade",
+ "Fade + Mute",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_pga_slew_time_enum,
+ ADAU1761_ALC_CTRL0, 6, 0x3, adau1761_pga_slew_time_text,
+ adau1761_pga_slew_time_values);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_function_enum,
+ ADAU1761_ALC_CTRL0, 0, adau1761_alc_function_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_hold_time_enum,
+ ADAU1761_ALC_CTRL1, 4, adau1761_alc_hold_time_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_attack_time_enum,
+ ADAU1761_ALC_CTRL2, 4, adau1761_alc_attack_time_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_decay_time_enum,
+ ADAU1761_ALC_CTRL2, 0, adau1761_alc_decay_time_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_ng_type_enum,
+ ADAU1761_ALC_CTRL3, 6, adau1761_alc_ng_type_text);
+
static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT,
4, 1, 0),
@@ -161,6 +270,22 @@ static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1,
ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv),
+
+ SOC_ENUM("PGA Capture Slew Time", adau1761_pga_slew_time_enum),
+
+ SOC_SINGLE_TLV("ALC Capture Max Gain Volume", ADAU1761_ALC_CTRL0,
+ 3, 7, 0, adau1761_alc_max_gain_tlv),
+ SOC_ENUM("ALC Capture Function", adau1761_alc_function_enum),
+ SOC_ENUM("ALC Capture Hold Time", adau1761_alc_hold_time_enum),
+ SOC_SINGLE_TLV("ALC Capture Target Volume", ADAU1761_ALC_CTRL1,
+ 0, 15, 0, adau1761_alc_target_tlv),
+ SOC_ENUM("ALC Capture Attack Time", adau1761_alc_decay_time_enum),
+ SOC_ENUM("ALC Capture Decay Time", adau1761_alc_attack_time_enum),
+ SOC_ENUM("ALC Capture Noise Gate Type", adau1761_alc_ng_type_enum),
+ SOC_SINGLE("ALC Capture Noise Gate Switch",
+ ADAU1761_ALC_CTRL3, 5, 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Noise Gate Threshold Volume",
+ ADAU1761_ALC_CTRL3, 0, 31, 0, adau1761_alc_ng_threshold_tlv),
};
static const struct snd_kcontrol_new adau1761_single_mode_controls[] = {
@@ -632,6 +757,10 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
case ADAU1761_DEJITTER:
case ADAU1761_CLK_ENABLE0:
case ADAU1761_CLK_ENABLE1:
+ case ADAU1761_ALC_CTRL0:
+ case ADAU1761_ALC_CTRL1:
+ case ADAU1761_ALC_CTRL2:
+ case ADAU1761_ALC_CTRL3:
return true;
default:
break;
diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c
new file mode 100644
index 000000000000..45a5d2dcc0f2
--- /dev/null
+++ b/sound/soc/codecs/adau7118-hw.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter Standalone Hw
+// driver
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include "adau7118.h"
+
+static int adau7118_probe_hw(struct platform_device *pdev)
+{
+ return adau7118_probe(&pdev->dev, NULL, true);
+}
+
+static const struct of_device_id adau7118_of_match[] = {
+ { .compatible = "adi,adau7118" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adau7118_of_match);
+
+static const struct platform_device_id adau7118_id[] = {
+ { .name = "adau7118" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, adau7118_id);
+
+static struct platform_driver adau7118_driver_hw = {
+ .driver = {
+ .name = "adau7118",
+ .of_match_table = adau7118_of_match,
+ },
+ .probe = adau7118_probe_hw,
+ .id_table = adau7118_id,
+};
+module_platform_driver(adau7118_driver_hw);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver for standalone hw mode");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c
new file mode 100644
index 000000000000..a8211362fe82
--- /dev/null
+++ b/sound/soc/codecs/adau7118-i2c.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "adau7118.h"
+
+static const struct reg_default adau7118_reg_defaults[] = {
+ { ADAU7118_REG_VENDOR_ID, 0x41 },
+ { ADAU7118_REG_DEVICE_ID1, 0x71 },
+ { ADAU7118_REG_DEVICE_ID2, 0x18 },
+ { ADAU7118_REG_REVISION_ID, 0x00 },
+ { ADAU7118_REG_ENABLES, 0x3F },
+ { ADAU7118_REG_DEC_RATIO_CLK_MAP, 0xC0 },
+ { ADAU7118_REG_HPF_CONTROL, 0xD0 },
+ { ADAU7118_REG_SPT_CTRL1, 0x41 },
+ { ADAU7118_REG_SPT_CTRL2, 0x00 },
+ { ADAU7118_REG_SPT_CX(0), 0x01 },
+ { ADAU7118_REG_SPT_CX(1), 0x11 },
+ { ADAU7118_REG_SPT_CX(2), 0x21 },
+ { ADAU7118_REG_SPT_CX(3), 0x31 },
+ { ADAU7118_REG_SPT_CX(4), 0x41 },
+ { ADAU7118_REG_SPT_CX(5), 0x51 },
+ { ADAU7118_REG_SPT_CX(6), 0x61 },
+ { ADAU7118_REG_SPT_CX(7), 0x71 },
+ { ADAU7118_REG_DRIVE_STRENGTH, 0x2a },
+ { ADAU7118_REG_RESET, 0x00 },
+};
+
+static const struct regmap_config adau7118_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_defaults = adau7118_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau7118_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = ADAU7118_REG_RESET,
+};
+
+static int adau7118_probe_i2c(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *map;
+
+ map = devm_regmap_init_i2c(i2c, &adau7118_regmap_config);
+ if (IS_ERR(map)) {
+ dev_err(&i2c->dev, "Failed to init regmap %ld\n", PTR_ERR(map));
+ return PTR_ERR(map);
+ }
+
+ return adau7118_probe(&i2c->dev, map, false);
+}
+
+static const struct of_device_id adau7118_of_match[] = {
+ { .compatible = "adi,adau7118" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adau7118_of_match);
+
+static const struct i2c_device_id adau7118_id[] = {
+ {"adau7118", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adau7118_id);
+
+static struct i2c_driver adau7118_driver = {
+ .driver = {
+ .name = "adau7118",
+ .of_match_table = adau7118_of_match,
+ },
+ .probe = adau7118_probe_i2c,
+ .id_table = adau7118_id,
+};
+module_i2c_driver(adau7118_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c
new file mode 100644
index 000000000000..841229dcbca1
--- /dev/null
+++ b/sound/soc/codecs/adau7118.c
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "adau7118.h"
+
+#define ADAU7118_DEC_RATIO_MASK GENMASK(1, 0)
+#define ADAU7118_DEC_RATIO(x) FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x)
+#define ADAU7118_CLK_MAP_MASK GENMASK(7, 4)
+#define ADAU7118_SLOT_WIDTH_MASK GENMASK(5, 4)
+#define ADAU7118_SLOT_WIDTH(x) FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x)
+#define ADAU7118_TRISTATE_MASK BIT(6)
+#define ADAU7118_TRISTATE(x) FIELD_PREP(ADAU7118_TRISTATE_MASK, x)
+#define ADAU7118_DATA_FMT_MASK GENMASK(3, 1)
+#define ADAU7118_DATA_FMT(x) FIELD_PREP(ADAU7118_DATA_FMT_MASK, x)
+#define ADAU7118_SAI_MODE_MASK BIT(0)
+#define ADAU7118_SAI_MODE(x) FIELD_PREP(ADAU7118_SAI_MODE_MASK, x)
+#define ADAU7118_LRCLK_BCLK_POL_MASK GENMASK(1, 0)
+#define ADAU7118_LRCLK_BCLK_POL(x) \
+ FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x)
+#define ADAU7118_SPT_SLOT_MASK GENMASK(7, 4)
+#define ADAU7118_SPT_SLOT(x) FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x)
+#define ADAU7118_FULL_SOFT_R_MASK BIT(1)
+#define ADAU7118_FULL_SOFT_R(x) FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x)
+
+struct adau7118_data {
+ struct regmap *map;
+ struct device *dev;
+ struct regulator *iovdd;
+ struct regulator *dvdd;
+ u32 slot_width;
+ u32 slots;
+ bool hw_mode;
+ bool right_j;
+};
+
+/* Input Enable */
+static const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = {
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0),
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0),
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0),
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget adau7118_widgets_sw[] = {
+ /* Input Enable Switches */
+ SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[0]),
+ SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[1]),
+ SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[2]),
+ SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[3]),
+
+ /* PDM Clocks */
+ SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0),
+
+ /* Output channels */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7),
+ 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7118_routes_sw[] = {
+ { "PDM0", "Capture Switch", "PDM_DAT0" },
+ { "PDM1", "Capture Switch", "PDM_DAT1" },
+ { "PDM2", "Capture Switch", "PDM_DAT2" },
+ { "PDM3", "Capture Switch", "PDM_DAT3" },
+ { "AIF1TX1", NULL, "PDM0" },
+ { "AIF1TX2", NULL, "PDM0" },
+ { "AIF1TX3", NULL, "PDM1" },
+ { "AIF1TX4", NULL, "PDM1" },
+ { "AIF1TX5", NULL, "PDM2" },
+ { "AIF1TX6", NULL, "PDM2" },
+ { "AIF1TX7", NULL, "PDM3" },
+ { "AIF1TX8", NULL, "PDM3" },
+ { "Capture", NULL, "PDM_CLK0" },
+ { "Capture", NULL, "PDM_CLK1" },
+};
+
+static const struct snd_soc_dapm_widget adau7118_widgets_hw[] = {
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7118_routes_hw[] = {
+ { "AIF1TX", NULL, "PDM_DAT0" },
+ { "AIF1TX", NULL, "PDM_DAT1" },
+ { "AIF1TX", NULL, "PDM_DAT2" },
+ { "AIF1TX", NULL, "PDM_DAT3" },
+};
+
+static const struct snd_soc_dapm_widget adau7118_widgets[] = {
+ SND_SOC_DAPM_INPUT("PDM_DAT0"),
+ SND_SOC_DAPM_INPUT("PDM_DAT1"),
+ SND_SOC_DAPM_INPUT("PDM_DAT2"),
+ SND_SOC_DAPM_INPUT("PDM_DAT3"),
+};
+
+static int adau7118_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int chan, ret;
+
+ dev_dbg(st->dev, "Set channel map, %d", tx_num);
+
+ for (chan = 0; chan < tx_num; chan++) {
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CX(chan),
+ ADAU7118_SPT_SLOT_MASK,
+ ADAU7118_SPT_SLOT(tx_slot[chan]));
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret = 0;
+ u32 regval;
+
+ dev_dbg(st->dev, "Set format, fmt:%d\n", fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(0));
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(1));
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ st->right_j = true;
+ break;
+ default:
+ dev_err(st->dev, "Invalid format %d",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ regval = ADAU7118_LRCLK_BCLK_POL(0);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval = ADAU7118_LRCLK_BCLK_POL(2);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval = ADAU7118_LRCLK_BCLK_POL(1);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval = ADAU7118_LRCLK_BCLK_POL(3);
+ break;
+ default:
+ dev_err(st->dev, "Invalid Inv mask %d",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL2,
+ ADAU7118_LRCLK_BCLK_POL_MASK,
+ regval);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ dev_dbg(st->dev, "Set tristate, %d\n", tristate);
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_TRISTATE_MASK,
+ ADAU7118_TRISTATE(tristate));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret = 0;
+ u32 regval;
+
+ dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width);
+
+ switch (slot_width) {
+ case 32:
+ regval = ADAU7118_SLOT_WIDTH(0);
+ break;
+ case 24:
+ regval = ADAU7118_SLOT_WIDTH(2);
+ break;
+ case 16:
+ regval = ADAU7118_SLOT_WIDTH(1);
+ break;
+ default:
+ dev_err(st->dev, "Invalid slot width:%d\n", slot_width);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_SLOT_WIDTH_MASK, regval);
+ if (ret < 0)
+ return ret;
+
+ st->slot_width = slot_width;
+ st->slots = slots;
+
+ return 0;
+}
+
+static int adau7118_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ u32 data_width = params_width(params), slots_width;
+ int ret;
+ u32 regval;
+
+ if (!st->slots) {
+ /* set stereo mode */
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_SAI_MODE_MASK,
+ ADAU7118_SAI_MODE(0));
+ if (ret < 0)
+ return ret;
+
+ slots_width = 32;
+ } else {
+ slots_width = st->slot_width;
+ }
+
+ if (data_width > slots_width) {
+ dev_err(st->dev, "Invalid data_width:%d, slots_width:%d",
+ data_width, slots_width);
+ return -EINVAL;
+ }
+
+ if (st->right_j) {
+ switch (slots_width - data_width) {
+ case 8:
+ /* delay bclck by 8 */
+ regval = ADAU7118_DATA_FMT(2);
+ break;
+ case 12:
+ /* delay bclck by 12 */
+ regval = ADAU7118_DATA_FMT(3);
+ break;
+ case 16:
+ /* delay bclck by 16 */
+ regval = ADAU7118_DATA_FMT(4);
+ break;
+ default:
+ dev_err(st->dev,
+ "Cannot set right_j setting, slot_w:%d, data_w:%d\n",
+ slots_width, data_width);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ regval);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adau7118_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adau7118_data *st = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ dev_dbg(st->dev, "Set bias level %d\n", level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) ==
+ SND_SOC_BIAS_OFF) {
+ /* power on */
+ ret = regulator_enable(st->iovdd);
+ if (ret)
+ return ret;
+
+ /* there's no timing constraints before enabling dvdd */
+ ret = regulator_enable(st->dvdd);
+ if (ret) {
+ regulator_disable(st->iovdd);
+ return ret;
+ }
+
+ if (st->hw_mode)
+ return 0;
+
+ regcache_cache_only(st->map, false);
+ /* sync cache */
+ ret = snd_soc_component_cache_sync(component);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* power off */
+ ret = regulator_disable(st->dvdd);
+ if (ret)
+ return ret;
+
+ ret = regulator_disable(st->iovdd);
+ if (ret)
+ return ret;
+
+ if (st->hw_mode)
+ return 0;
+
+ /* cache only */
+ regcache_mark_dirty(st->map);
+ regcache_cache_only(st->map, true);
+
+ break;
+ }
+
+ return ret;
+}
+
+static int adau7118_component_probe(struct snd_soc_component *component)
+{
+ struct adau7118_data *st = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ int ret = 0;
+
+ if (st->hw_mode) {
+ ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw,
+ ARRAY_SIZE(adau7118_widgets_hw));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw,
+ ARRAY_SIZE(adau7118_routes_hw));
+ } else {
+ snd_soc_component_init_regmap(component, st->map);
+ ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw,
+ ARRAY_SIZE(adau7118_widgets_sw));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw,
+ ARRAY_SIZE(adau7118_routes_sw));
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops adau7118_ops = {
+ .hw_params = adau7118_hw_params,
+ .set_channel_map = adau7118_set_channel_map,
+ .set_fmt = adau7118_set_fmt,
+ .set_tdm_slot = adau7118_set_tdm_slot,
+ .set_tristate = adau7118_set_tristate,
+};
+
+static struct snd_soc_dai_driver adau7118_dai = {
+ .name = "adau7118-hifi-capture",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 4000,
+ .rate_max = 192000,
+ .sig_bits = 24,
+ },
+};
+
+static const struct snd_soc_component_driver adau7118_component_driver = {
+ .probe = adau7118_component_probe,
+ .set_bias_level = adau7118_set_bias_level,
+ .dapm_widgets = adau7118_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau7118_widgets),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static void adau7118_regulator_disable(void *data)
+{
+ struct adau7118_data *st = data;
+ int ret;
+ /*
+ * If we fail to disable DVDD, don't bother in trying IOVDD. We
+ * actually don't want to be left in the situation where DVDD
+ * is enabled and IOVDD is disabled.
+ */
+ ret = regulator_disable(st->dvdd);
+ if (ret)
+ return;
+
+ regulator_disable(st->iovdd);
+}
+
+static int adau7118_regulator_setup(struct adau7118_data *st)
+{
+ st->iovdd = devm_regulator_get(st->dev, "iovdd");
+ if (IS_ERR(st->iovdd)) {
+ dev_err(st->dev, "Could not get iovdd: %ld\n",
+ PTR_ERR(st->iovdd));
+ return PTR_ERR(st->iovdd);
+ }
+
+ st->dvdd = devm_regulator_get(st->dev, "dvdd");
+ if (IS_ERR(st->dvdd)) {
+ dev_err(st->dev, "Could not get dvdd: %ld\n",
+ PTR_ERR(st->dvdd));
+ return PTR_ERR(st->dvdd);
+ }
+ /* just assume the device is in reset */
+ if (!st->hw_mode) {
+ regcache_mark_dirty(st->map);
+ regcache_cache_only(st->map, true);
+ }
+
+ return devm_add_action_or_reset(st->dev, adau7118_regulator_disable,
+ st);
+}
+
+static int adau7118_parset_dt(const struct adau7118_data *st)
+{
+ int ret;
+ u32 dec_ratio = 0;
+ /* 4 inputs */
+ u32 clk_map[4], regval;
+
+ if (st->hw_mode)
+ return 0;
+
+ ret = device_property_read_u32(st->dev, "adi,decimation-ratio",
+ &dec_ratio);
+ if (!ret) {
+ switch (dec_ratio) {
+ case 64:
+ regval = ADAU7118_DEC_RATIO(0);
+ break;
+ case 32:
+ regval = ADAU7118_DEC_RATIO(1);
+ break;
+ case 16:
+ regval = ADAU7118_DEC_RATIO(2);
+ break;
+ default:
+ dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(st->map,
+ ADAU7118_REG_DEC_RATIO_CLK_MAP,
+ ADAU7118_DEC_RATIO_MASK, regval);
+ if (ret)
+ return ret;
+ }
+
+ ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map",
+ clk_map, ARRAY_SIZE(clk_map));
+ if (!ret) {
+ int pdm;
+ u32 _clk_map = 0;
+
+ for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++)
+ _clk_map |= (clk_map[pdm] << (pdm + 4));
+
+ ret = regmap_update_bits(st->map,
+ ADAU7118_REG_DEC_RATIO_CLK_MAP,
+ ADAU7118_CLK_MAP_MASK, _clk_map);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode)
+{
+ struct adau7118_data *st;
+ int ret;
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->dev = dev;
+ st->hw_mode = hw_mode;
+ dev_set_drvdata(dev, st);
+
+ if (!hw_mode) {
+ st->map = map;
+ adau7118_dai.ops = &adau7118_ops;
+ /*
+ * Perform a full soft reset. This will set all register's
+ * with their reset values.
+ */
+ ret = regmap_update_bits(map, ADAU7118_REG_RESET,
+ ADAU7118_FULL_SOFT_R_MASK,
+ ADAU7118_FULL_SOFT_R(1));
+ if (ret)
+ return ret;
+ }
+
+ ret = adau7118_parset_dt(st);
+ if (ret)
+ return ret;
+
+ ret = adau7118_regulator_setup(st);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(dev,
+ &adau7118_component_driver,
+ &adau7118_dai, 1);
+}
+EXPORT_SYMBOL_GPL(adau7118_probe);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau7118.h b/sound/soc/codecs/adau7118.h
new file mode 100644
index 000000000000..c65679a4dff1
--- /dev/null
+++ b/sound/soc/codecs/adau7118.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_ADAU7118_H
+#define _LINUX_ADAU7118_H
+
+struct regmap;
+struct device;
+
+/* register map */
+#define ADAU7118_REG_VENDOR_ID 0x00
+#define ADAU7118_REG_DEVICE_ID1 0x01
+#define ADAU7118_REG_DEVICE_ID2 0x02
+#define ADAU7118_REG_REVISION_ID 0x03
+#define ADAU7118_REG_ENABLES 0x04
+#define ADAU7118_REG_DEC_RATIO_CLK_MAP 0x05
+#define ADAU7118_REG_HPF_CONTROL 0x06
+#define ADAU7118_REG_SPT_CTRL1 0x07
+#define ADAU7118_REG_SPT_CTRL2 0x08
+#define ADAU7118_REG_SPT_CX(num) (0x09 + (num))
+#define ADAU7118_REG_DRIVE_STRENGTH 0x11
+#define ADAU7118_REG_RESET 0x12
+
+int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode);
+
+#endif
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c
index 3c1bd24a1057..dd14caf9091a 100644
--- a/sound/soc/codecs/cros_ec_codec.c
+++ b/sound/soc/codecs/cros_ec_codec.c
@@ -1,15 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Driver for ChromeOS Embedded Controller codec.
+ * Copyright 2019 Google, Inc.
+ *
+ * ChromeOS Embedded Controller codec driver.
*
* This driver uses the cros-ec interface to communicate with the ChromeOS
* EC for audio function.
*/
+#include <crypto/hash.h>
+#include <crypto/sha.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
@@ -18,92 +26,279 @@
#include <sound/soc.h>
#include <sound/tlv.h>
-#define DRV_NAME "cros-ec-codec"
-
-/**
- * struct cros_ec_codec_data - ChromeOS EC codec driver data.
- * @dev: Device structure used in sysfs.
- * @ec_device: cros_ec_device structure to talk to the physical device.
- * @component: Pointer to the component.
- * @max_dmic_gain: Maximum gain in dB supported by EC codec.
- */
-struct cros_ec_codec_data {
+struct cros_ec_codec_priv {
struct device *dev;
struct cros_ec_device *ec_device;
- struct snd_soc_component *component;
- unsigned int max_dmic_gain;
+
+ /* common */
+ uint32_t ec_capabilities;
+
+ uint64_t ec_shm_addr;
+ uint32_t ec_shm_len;
+
+ uint64_t ap_shm_phys_addr;
+ uint32_t ap_shm_len;
+ uint64_t ap_shm_addr;
+ uint64_t ap_shm_last_alloc;
+
+ /* DMIC */
+ atomic_t dmic_probed;
+
+ /* WoV */
+ bool wov_enabled;
+ uint8_t *wov_audio_shm_p;
+ uint32_t wov_audio_shm_len;
+ uint8_t wov_audio_shm_type;
+ uint8_t *wov_lang_shm_p;
+ uint32_t wov_lang_shm_len;
+ uint8_t wov_lang_shm_type;
+
+ struct mutex wov_dma_lock;
+ uint8_t wov_buf[64000];
+ uint32_t wov_rp, wov_wp;
+ size_t wov_dma_offset;
+ bool wov_burst_read;
+ struct snd_pcm_substream *wov_substream;
+ struct delayed_work wov_copy_work;
+ struct notifier_block wov_notifier;
};
-static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0);
+static int ec_codec_capable(struct cros_ec_codec_priv *priv, uint8_t cap)
+{
+ return priv->ec_capabilities & BIT(cap);
+}
-static int ec_command_get_gain(struct snd_soc_component *component,
- struct ec_param_codec_i2s *param,
- struct ec_codec_i2s_gain *resp)
+static int send_ec_host_command(struct cros_ec_device *ec_dev, uint32_t cmd,
+ uint8_t *out, size_t outsize,
+ uint8_t *in, size_t insize)
{
- struct cros_ec_codec_data *codec_data =
- snd_soc_component_get_drvdata(component);
- struct cros_ec_device *ec_device = codec_data->ec_device;
- u8 buffer[sizeof(struct cros_ec_command) +
- max(sizeof(struct ec_param_codec_i2s),
- sizeof(struct ec_codec_i2s_gain))];
- struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
int ret;
+ struct cros_ec_command *msg;
+
+ msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
msg->version = 0;
- msg->command = EC_CMD_CODEC_I2S;
- msg->outsize = sizeof(struct ec_param_codec_i2s);
- msg->insize = sizeof(struct ec_codec_i2s_gain);
+ msg->command = cmd;
+ msg->outsize = outsize;
+ msg->insize = insize;
- memcpy(msg->data, param, msg->outsize);
+ if (outsize)
+ memcpy(msg->data, out, outsize);
- ret = cros_ec_cmd_xfer_status(ec_device, msg);
- if (ret > 0)
- memcpy(resp, msg->data, msg->insize);
+ ret = cros_ec_cmd_xfer_status(ec_dev, msg);
+ if (ret < 0)
+ goto error;
+
+ if (insize)
+ memcpy(in, msg->data, insize);
+ ret = 0;
+error:
+ kfree(msg);
return ret;
}
-/*
- * Wrapper for EC command without response.
- */
-static int ec_command_no_resp(struct snd_soc_component *component,
- struct ec_param_codec_i2s *param)
+static int calculate_sha256(struct cros_ec_codec_priv *priv,
+ uint8_t *buf, uint32_t size, uint8_t *digest)
{
- struct cros_ec_codec_data *codec_data =
+ struct crypto_shash *tfm;
+
+ tfm = crypto_alloc_shash("sha256", CRYPTO_ALG_TYPE_SHASH, 0);
+ if (IS_ERR(tfm)) {
+ dev_err(priv->dev, "can't alloc shash\n");
+ return PTR_ERR(tfm);
+ }
+
+ {
+ SHASH_DESC_ON_STACK(desc, tfm);
+
+ desc->tfm = tfm;
+
+ crypto_shash_digest(desc, buf, size, digest);
+ shash_desc_zero(desc);
+ }
+
+ crypto_free_shash(tfm);
+
+#ifdef DEBUG
+ {
+ char digest_str[65];
+
+ bin2hex(digest_str, digest, 32);
+ digest_str[64] = 0;
+ dev_dbg(priv->dev, "hash=%s\n", digest_str);
+ }
+#endif
+
+ return 0;
+}
+
+static int dmic_get_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cros_ec_codec_priv *priv =
snd_soc_component_get_drvdata(component);
- struct cros_ec_device *ec_device = codec_data->ec_device;
- u8 buffer[sizeof(struct cros_ec_command) +
- sizeof(struct ec_param_codec_i2s)];
- struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
+ struct ec_param_ec_codec_dmic p;
+ struct ec_response_ec_codec_dmic_get_gain_idx r;
+ int ret;
- msg->version = 0;
- msg->command = EC_CMD_CODEC_I2S;
- msg->outsize = sizeof(struct ec_param_codec_i2s);
- msg->insize = 0;
+ p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
+ p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret < 0)
+ return ret;
+ ucontrol->value.integer.value[0] = r.gain;
+
+ p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
+ p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret < 0)
+ return ret;
+ ucontrol->value.integer.value[1] = r.gain;
+
+ return 0;
+}
+
+static int dmic_put_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *control =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int max_dmic_gain = control->max;
+ int left = ucontrol->value.integer.value[0];
+ int right = ucontrol->value.integer.value[1];
+ struct ec_param_ec_codec_dmic p;
+ int ret;
+
+ if (left > max_dmic_gain || right > max_dmic_gain)
+ return -EINVAL;
+
+ dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
+
+ p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
+ p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
+ p.set_gain_idx_param.gain = left;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
+ p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
+ p.set_gain_idx_param.gain = right;
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0);
+
+enum {
+ DMIC_CTL_GAIN = 0,
+};
+
+static struct snd_kcontrol_new dmic_controls[] = {
+ [DMIC_CTL_GAIN] =
+ SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM,
+ 0, 0, 0, dmic_get_gain, dmic_put_gain,
+ dmic_gain_tlv),
+};
+
+static int dmic_probe(struct snd_soc_component *component)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct device *dev = priv->dev;
+ struct soc_mixer_control *control;
+ struct ec_param_ec_codec_dmic p;
+ struct ec_response_ec_codec_dmic_get_max_gain r;
+ int ret;
+
+ if (!atomic_add_unless(&priv->dmic_probed, 1, 1))
+ return 0;
+
+ p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN;
+
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret < 0) {
+ dev_warn(dev, "get_max_gain() unsupported\n");
+ return 0;
+ }
- memcpy(msg->data, param, msg->outsize);
+ dev_dbg(dev, "max gain = %d\n", r.max_gain);
- return cros_ec_cmd_xfer_status(ec_device, msg);
+ control = (struct soc_mixer_control *)
+ dmic_controls[DMIC_CTL_GAIN].private_value;
+ control->max = r.max_gain;
+ control->platform_max = r.max_gain;
+
+ return snd_soc_add_component_controls(component,
+ &dmic_controls[DMIC_CTL_GAIN], 1);
}
-static int set_i2s_config(struct snd_soc_component *component,
- enum ec_i2s_config i2s_config)
+static int i2s_rx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
- struct ec_param_codec_i2s param;
+ struct snd_soc_component *component = dai->component;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_i2s_rx p;
+ enum ec_codec_i2s_rx_sample_depth depth;
+ int ret;
- dev_dbg(component->dev, "%s set I2S format to %u\n", __func__,
- i2s_config);
+ if (params_rate(params) != 48000)
+ return -EINVAL;
- param.cmd = EC_CODEC_I2S_SET_CONFIG;
- param.i2s_config = i2s_config;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "set depth to %u\n", depth);
+
+ p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH;
+ p.set_sample_depth_param.depth = depth;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret < 0)
+ return ret;
- return ec_command_no_resp(component, &param);
+ dev_dbg(component->dev, "set bclk to %u\n",
+ snd_soc_params_to_bclk(params));
+
+ p.cmd = EC_CODEC_I2S_RX_SET_BCLK;
+ p.set_bclk_param.bclk = snd_soc_params_to_bclk(params);
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
}
-static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
- enum ec_i2s_config i2s_config;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_i2s_rx p;
+ enum ec_codec_i2s_rx_daifmt daifmt;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
@@ -121,300 +316,727 @@ static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- i2s_config = EC_DAI_FMT_I2S;
+ daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S;
break;
-
case SND_SOC_DAIFMT_RIGHT_J:
- i2s_config = EC_DAI_FMT_RIGHT_J;
+ daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J;
break;
-
case SND_SOC_DAIFMT_LEFT_J:
- i2s_config = EC_DAI_FMT_LEFT_J;
+ daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J;
break;
+ default:
+ return -EINVAL;
+ }
- case SND_SOC_DAIFMT_DSP_A:
- i2s_config = EC_DAI_FMT_PCM_A;
- break;
+ dev_dbg(component->dev, "set format to %u\n", daifmt);
- case SND_SOC_DAIFMT_DSP_B:
- i2s_config = EC_DAI_FMT_PCM_B;
- break;
+ p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT;
+ p.set_daifmt_param.daifmt = daifmt;
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static const struct snd_soc_dai_ops i2s_rx_dai_ops = {
+ .hw_params = i2s_rx_hw_params,
+ .set_fmt = i2s_rx_set_fmt,
+};
+static int i2s_rx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_i2s_rx p;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(component->dev, "enable I2S RX\n");
+ p.cmd = EC_CODEC_I2S_RX_ENABLE;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_dbg(component->dev, "disable I2S RX\n");
+ p.cmd = EC_CODEC_I2S_RX_DISABLE;
+ break;
default:
- return -EINVAL;
+ return 0;
}
- return set_i2s_config(component, i2s_config);
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = {
+ {"I2S RX", NULL, "DMIC"},
+ {"I2S RX", NULL, "I2S RX Enable"},
+};
+
+static struct snd_soc_dai_driver i2s_rx_dai_driver = {
+ .name = "EC Codec I2S RX",
+ .capture = {
+ .stream_name = "I2S Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &i2s_rx_dai_ops,
+};
+
+static int i2s_rx_probe(struct snd_soc_component *component)
+{
+ return dmic_probe(component);
}
-static int set_i2s_sample_depth(struct snd_soc_component *component,
- enum ec_sample_depth_value depth)
+static const struct snd_soc_component_driver i2s_rx_component_driver = {
+ .probe = i2s_rx_probe,
+ .dapm_widgets = i2s_rx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets),
+ .dapm_routes = i2s_rx_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes),
+};
+
+static void *wov_map_shm(struct cros_ec_codec_priv *priv,
+ uint8_t shm_id, uint32_t *len, uint8_t *type)
{
- struct ec_param_codec_i2s param;
+ struct ec_param_ec_codec p;
+ struct ec_response_ec_codec_get_shm_addr r;
+ uint32_t req, offset;
+
+ p.cmd = EC_CODEC_GET_SHM_ADDR;
+ p.get_shm_addr_param.shm_id = shm_id;
+ if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r)) < 0) {
+ dev_err(priv->dev, "failed to EC_CODEC_GET_SHM_ADDR\n");
+ return NULL;
+ }
- dev_dbg(component->dev, "%s set depth to %u\n", __func__, depth);
+ dev_dbg(priv->dev, "phys_addr=%#llx, len=%#x\n", r.phys_addr, r.len);
+
+ *len = r.len;
+ *type = r.type;
+
+ switch (r.type) {
+ case EC_CODEC_SHM_TYPE_EC_RAM:
+ return (void __force *)devm_ioremap_wc(priv->dev,
+ r.phys_addr + priv->ec_shm_addr, r.len);
+ case EC_CODEC_SHM_TYPE_SYSTEM_RAM:
+ if (r.phys_addr) {
+ dev_err(priv->dev, "unknown status\n");
+ return NULL;
+ }
+
+ req = round_up(r.len, PAGE_SIZE);
+ dev_dbg(priv->dev, "round up from %u to %u\n", r.len, req);
+
+ if (priv->ap_shm_last_alloc + req >
+ priv->ap_shm_phys_addr + priv->ap_shm_len) {
+ dev_err(priv->dev, "insufficient space for AP SHM\n");
+ return NULL;
+ }
+
+ dev_dbg(priv->dev, "alloc AP SHM addr=%#llx, len=%#x\n",
+ priv->ap_shm_last_alloc, req);
+
+ p.cmd = EC_CODEC_SET_SHM_ADDR;
+ p.set_shm_addr_param.phys_addr = priv->ap_shm_last_alloc;
+ p.set_shm_addr_param.len = req;
+ p.set_shm_addr_param.shm_id = shm_id;
+ if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
+ (uint8_t *)&p, sizeof(p),
+ NULL, 0) < 0) {
+ dev_err(priv->dev, "failed to EC_CODEC_SET_SHM_ADDR\n");
+ return NULL;
+ }
+
+ /*
+ * Note: EC codec only requests for `r.len' but we allocate
+ * round up PAGE_SIZE `req'.
+ */
+ offset = priv->ap_shm_last_alloc - priv->ap_shm_phys_addr;
+ priv->ap_shm_last_alloc += req;
+
+ return (void *)(uintptr_t)(priv->ap_shm_addr + offset);
+ default:
+ return NULL;
+ }
+}
- param.cmd = EC_CODEC_SET_SAMPLE_DEPTH;
- param.depth = depth;
+static bool wov_queue_full(struct cros_ec_codec_priv *priv)
+{
+ return ((priv->wov_wp + 1) % sizeof(priv->wov_buf)) == priv->wov_rp;
+}
- return ec_command_no_resp(component, &param);
+static size_t wov_queue_size(struct cros_ec_codec_priv *priv)
+{
+ if (priv->wov_wp >= priv->wov_rp)
+ return priv->wov_wp - priv->wov_rp;
+ else
+ return sizeof(priv->wov_buf) - priv->wov_rp + priv->wov_wp;
}
-static int set_i2s_bclk(struct snd_soc_component *component, uint32_t bclk)
+static void wov_queue_dequeue(struct cros_ec_codec_priv *priv, size_t len)
{
- struct ec_param_codec_i2s param;
+ struct snd_pcm_runtime *runtime = priv->wov_substream->runtime;
+ size_t req;
+
+ while (len) {
+ req = min(len, runtime->dma_bytes - priv->wov_dma_offset);
+ if (priv->wov_wp >= priv->wov_rp)
+ req = min(req, (size_t)priv->wov_wp - priv->wov_rp);
+ else
+ req = min(req, sizeof(priv->wov_buf) - priv->wov_rp);
+
+ memcpy(runtime->dma_area + priv->wov_dma_offset,
+ priv->wov_buf + priv->wov_rp, req);
- dev_dbg(component->dev, "%s set i2s bclk to %u\n", __func__, bclk);
+ priv->wov_dma_offset += req;
+ if (priv->wov_dma_offset == runtime->dma_bytes)
+ priv->wov_dma_offset = 0;
- param.cmd = EC_CODEC_I2S_SET_BCLK;
- param.bclk = bclk;
+ priv->wov_rp += req;
+ if (priv->wov_rp == sizeof(priv->wov_buf))
+ priv->wov_rp = 0;
- return ec_command_no_resp(component, &param);
+ len -= req;
+ }
+
+ snd_pcm_period_elapsed(priv->wov_substream);
}
-static int cros_ec_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static void wov_queue_try_dequeue(struct cros_ec_codec_priv *priv)
{
- struct snd_soc_component *component = dai->component;
- unsigned int rate, bclk;
- int ret;
+ size_t period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream);
- rate = params_rate(params);
- if (rate != 48000)
- return -EINVAL;
+ while (period_bytes && wov_queue_size(priv) >= period_bytes) {
+ wov_queue_dequeue(priv, period_bytes);
+ period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream);
+ }
+}
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_16);
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_24);
- break;
- default:
- return -EINVAL;
+static void wov_queue_enqueue(struct cros_ec_codec_priv *priv,
+ uint8_t *addr, size_t len, bool iomem)
+{
+ size_t req;
+
+ while (len) {
+ if (wov_queue_full(priv)) {
+ wov_queue_try_dequeue(priv);
+
+ if (wov_queue_full(priv)) {
+ dev_err(priv->dev, "overrun detected\n");
+ return;
+ }
+ }
+
+ if (priv->wov_wp >= priv->wov_rp)
+ req = sizeof(priv->wov_buf) - priv->wov_wp;
+ else
+ /* Note: waste 1-byte to differentiate full and empty */
+ req = priv->wov_rp - priv->wov_wp - 1;
+ req = min(req, len);
+
+ if (iomem)
+ memcpy_fromio(priv->wov_buf + priv->wov_wp,
+ (void __force __iomem *)addr, req);
+ else
+ memcpy(priv->wov_buf + priv->wov_wp, addr, req);
+
+ priv->wov_wp += req;
+ if (priv->wov_wp == sizeof(priv->wov_buf))
+ priv->wov_wp = 0;
+
+ addr += req;
+ len -= req;
}
- if (ret < 0)
+
+ wov_queue_try_dequeue(priv);
+}
+
+static int wov_read_audio_shm(struct cros_ec_codec_priv *priv)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_response_ec_codec_wov_read_audio_shm r;
+ int ret;
+
+ p.cmd = EC_CODEC_WOV_READ_AUDIO_SHM;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret) {
+ dev_err(priv->dev, "failed to EC_CODEC_WOV_READ_AUDIO_SHM\n");
return ret;
+ }
- bclk = snd_soc_params_to_bclk(params);
- return set_i2s_bclk(component, bclk);
+ if (!r.len)
+ dev_dbg(priv->dev, "no data, sleep\n");
+ else
+ wov_queue_enqueue(priv, priv->wov_audio_shm_p + r.offset, r.len,
+ priv->wov_audio_shm_type == EC_CODEC_SHM_TYPE_EC_RAM);
+ return -EAGAIN;
}
-static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = {
- .hw_params = cros_ec_i2s_hw_params,
- .set_fmt = cros_ec_i2s_set_dai_fmt,
-};
+static int wov_read_audio(struct cros_ec_codec_priv *priv)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_response_ec_codec_wov_read_audio r;
+ int remain = priv->wov_burst_read ? 16000 : 320;
+ int ret;
-static struct snd_soc_dai_driver cros_ec_dai[] = {
- {
- .name = "cros_ec_codec I2S",
- .id = 0,
- .capture = {
- .stream_name = "I2S Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- },
- .ops = &cros_ec_i2s_dai_ops,
+ while (remain >= 0) {
+ p.cmd = EC_CODEC_WOV_READ_AUDIO;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to EC_CODEC_WOV_READ_AUDIO\n");
+ return ret;
+ }
+
+ if (!r.len) {
+ dev_dbg(priv->dev, "no data, sleep\n");
+ priv->wov_burst_read = false;
+ break;
+ }
+
+ wov_queue_enqueue(priv, r.buf, r.len, false);
+ remain -= r.len;
}
-};
-static int get_ec_mic_gain(struct snd_soc_component *component,
- u8 *left, u8 *right)
+ return -EAGAIN;
+}
+
+static void wov_copy_work(struct work_struct *w)
{
- struct ec_param_codec_i2s param;
- struct ec_codec_i2s_gain resp;
+ struct cros_ec_codec_priv *priv =
+ container_of(w, struct cros_ec_codec_priv, wov_copy_work.work);
int ret;
- param.cmd = EC_CODEC_GET_GAIN;
+ mutex_lock(&priv->wov_dma_lock);
+ if (!priv->wov_substream) {
+ dev_warn(priv->dev, "no pcm substream\n");
+ goto leave;
+ }
- ret = ec_command_get_gain(component, &param, &resp);
- if (ret < 0)
- return ret;
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM))
+ ret = wov_read_audio_shm(priv);
+ else
+ ret = wov_read_audio(priv);
+
+ if (ret == -EAGAIN)
+ schedule_delayed_work(&priv->wov_copy_work,
+ msecs_to_jiffies(10));
+ else if (ret)
+ dev_err(priv->dev, "failed to read audio data\n");
+leave:
+ mutex_unlock(&priv->wov_dma_lock);
+}
- *left = resp.left;
- *right = resp.right;
+static int wov_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c);
+ ucontrol->value.integer.value[0] = priv->wov_enabled;
return 0;
}
-static int mic_gain_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int wov_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *component =
- snd_soc_kcontrol_component(kcontrol);
- u8 left, right;
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c);
+ int enabled = ucontrol->value.integer.value[0];
+ struct ec_param_ec_codec_wov p;
int ret;
- ret = get_ec_mic_gain(component, &left, &right);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[0] = left;
- ucontrol->value.integer.value[1] = right;
+ if (priv->wov_enabled != enabled) {
+ if (enabled)
+ p.cmd = EC_CODEC_WOV_ENABLE;
+ else
+ p.cmd = EC_CODEC_WOV_DISABLE;
+
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to %s wov\n",
+ enabled ? "enable" : "disable");
+ return ret;
+ }
+
+ priv->wov_enabled = enabled;
+ }
return 0;
}
-static int set_ec_mic_gain(struct snd_soc_component *component,
- u8 left, u8 right)
+static int wov_set_lang_shm(struct cros_ec_codec_priv *priv,
+ uint8_t *buf, size_t size, uint8_t *digest)
{
- struct ec_param_codec_i2s param;
+ struct ec_param_ec_codec_wov p;
+ struct ec_param_ec_codec_wov_set_lang_shm *pp = &p.set_lang_shm_param;
+ int ret;
- dev_dbg(component->dev, "%s set mic gain to %u, %u\n",
- __func__, left, right);
+ if (size > priv->wov_lang_shm_len) {
+ dev_err(priv->dev, "no enough SHM size: %d\n",
+ priv->wov_lang_shm_len);
+ return -EIO;
+ }
- param.cmd = EC_CODEC_SET_GAIN;
- param.gain.left = left;
- param.gain.right = right;
+ switch (priv->wov_lang_shm_type) {
+ case EC_CODEC_SHM_TYPE_EC_RAM:
+ memcpy_toio((void __force __iomem *)priv->wov_lang_shm_p,
+ buf, size);
+ memset_io((void __force __iomem *)priv->wov_lang_shm_p + size,
+ 0, priv->wov_lang_shm_len - size);
+ break;
+ case EC_CODEC_SHM_TYPE_SYSTEM_RAM:
+ memcpy(priv->wov_lang_shm_p, buf, size);
+ memset(priv->wov_lang_shm_p + size, 0,
+ priv->wov_lang_shm_len - size);
- return ec_command_no_resp(component, &param);
+ /* make sure write to memory before calling host command */
+ wmb();
+ break;
+ }
+
+ p.cmd = EC_CODEC_WOV_SET_LANG_SHM;
+ memcpy(pp->hash, digest, SHA256_DIGEST_SIZE);
+ pp->total_len = size;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG_SHM\n");
+ return ret;
+ }
+
+ return 0;
}
-static int mic_gain_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int wov_set_lang(struct cros_ec_codec_priv *priv,
+ uint8_t *buf, size_t size, uint8_t *digest)
{
- struct snd_soc_component *component =
- snd_soc_kcontrol_component(kcontrol);
- struct cros_ec_codec_data *codec_data =
- snd_soc_component_get_drvdata(component);
- int left = ucontrol->value.integer.value[0];
- int right = ucontrol->value.integer.value[1];
- unsigned int max_dmic_gain = codec_data->max_dmic_gain;
+ struct ec_param_ec_codec_wov p;
+ struct ec_param_ec_codec_wov_set_lang *pp = &p.set_lang_param;
+ size_t i, req;
+ int ret;
- if (left > max_dmic_gain || right > max_dmic_gain)
- return -EINVAL;
+ for (i = 0; i < size; i += req) {
+ req = min(size - i, ARRAY_SIZE(pp->buf));
+
+ p.cmd = EC_CODEC_WOV_SET_LANG;
+ memcpy(pp->hash, digest, SHA256_DIGEST_SIZE);
+ pp->total_len = size;
+ pp->offset = i;
+ memcpy(pp->buf, buf + i, req);
+ pp->len = req;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG\n");
+ return ret;
+ }
+ }
- return set_ec_mic_gain(component, (u8)left, (u8)right);
+ return 0;
}
-static struct snd_kcontrol_new mic_gain_control =
- SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, 0, 0, 0,
- mic_gain_get, mic_gain_put, ec_mic_gain_tlv);
-
-static int enable_i2s(struct snd_soc_component *component, int enable)
+static int wov_hotword_model_put(struct snd_kcontrol *kcontrol,
+ const unsigned int __user *bytes,
+ unsigned int size)
{
- struct ec_param_codec_i2s param;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_wov p;
+ struct ec_response_ec_codec_wov_get_lang r;
+ uint8_t digest[SHA256_DIGEST_SIZE];
+ uint8_t *buf;
+ int ret;
+
+ /* Skips the TLV header. */
+ bytes += 2;
+ size -= 8;
+
+ dev_dbg(priv->dev, "%s: size=%d\n", __func__, size);
+
+ buf = memdup_user(bytes, size);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ ret = calculate_sha256(priv, buf, size, digest);
+ if (ret)
+ goto leave;
- dev_dbg(component->dev, "%s set i2s to %u\n", __func__, enable);
+ p.cmd = EC_CODEC_WOV_GET_LANG;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret)
+ goto leave;
+
+ if (memcmp(digest, r.hash, SHA256_DIGEST_SIZE) == 0) {
+ dev_dbg(priv->dev, "not updated");
+ goto leave;
+ }
- param.cmd = EC_CODEC_I2S_ENABLE;
- param.i2s_enable = enable;
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM))
+ ret = wov_set_lang_shm(priv, buf, size, digest);
+ else
+ ret = wov_set_lang(priv, buf, size, digest);
- return ec_command_no_resp(component, &param);
+leave:
+ kfree(buf);
+ return ret;
}
-static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+static struct snd_kcontrol_new wov_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Wake-on-Voice Switch", 0,
+ wov_enable_get, wov_enable_put),
+ SND_SOC_BYTES_TLV("Hotword Model", 0x11000, NULL,
+ wov_hotword_model_put),
+};
+
+static struct snd_soc_dai_driver wov_dai_driver = {
+ .name = "Wake on Voice",
+ .capture = {
+ .stream_name = "WoV Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static int wov_host_event(struct notifier_block *nb,
+ unsigned long queued_during_suspend, void *notify)
{
- struct snd_soc_component *component =
- snd_soc_dapm_to_component(w->dapm);
+ struct cros_ec_codec_priv *priv =
+ container_of(nb, struct cros_ec_codec_priv, wov_notifier);
+ u32 host_event;
+
+ dev_dbg(priv->dev, "%s\n", __func__);
+
+ host_event = cros_ec_get_host_event(priv->ec_device);
+ if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_WOV)) {
+ schedule_delayed_work(&priv->wov_copy_work, 0);
+ return NOTIFY_OK;
+ } else {
+ return NOTIFY_DONE;
+ }
+}
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- dev_dbg(component->dev,
- "%s got SND_SOC_DAPM_PRE_PMU event\n", __func__);
- return enable_i2s(component, 1);
+static int wov_probe(struct snd_soc_component *component)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ int ret;
- case SND_SOC_DAPM_PRE_PMD:
- dev_dbg(component->dev,
- "%s got SND_SOC_DAPM_PRE_PMD event\n", __func__);
- return enable_i2s(component, 0);
+ mutex_init(&priv->wov_dma_lock);
+ INIT_DELAYED_WORK(&priv->wov_copy_work, wov_copy_work);
+
+ priv->wov_notifier.notifier_call = wov_host_event;
+ ret = blocking_notifier_chain_register(
+ &priv->ec_device->event_notifier, &priv->wov_notifier);
+ if (ret)
+ return ret;
+
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) {
+ priv->wov_lang_shm_p = wov_map_shm(priv,
+ EC_CODEC_SHM_ID_WOV_LANG,
+ &priv->wov_lang_shm_len,
+ &priv->wov_lang_shm_type);
+ if (!priv->wov_lang_shm_p)
+ return -EFAULT;
}
- return 0;
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) {
+ priv->wov_audio_shm_p = wov_map_shm(priv,
+ EC_CODEC_SHM_ID_WOV_AUDIO,
+ &priv->wov_audio_shm_len,
+ &priv->wov_audio_shm_type);
+ if (!priv->wov_audio_shm_p)
+ return -EFAULT;
+ }
+
+ return dmic_probe(component);
}
-/*
- * The goal of this DAPM route is to turn on/off I2S using EC
- * host command when capture stream is started/stopped.
- */
-static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets[] = {
- SND_SOC_DAPM_INPUT("DMIC"),
+static void wov_remove(struct snd_soc_component *component)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
- /*
- * Control EC to enable/disable I2S.
- */
- SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM,
- 0, 0, cros_ec_i2s_enable_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ blocking_notifier_chain_unregister(
+ &priv->ec_device->event_notifier, &priv->wov_notifier);
+}
- SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
-};
+static int wov_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hw_param = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = 0x20000 / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .buffer_bytes_max = 0x20000,
+ };
+
+ return snd_soc_set_runtime_hwparams(substream, &hw_param);
+}
-static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes[] = {
- { "I2STX", NULL, "DMIC" },
- { "I2STX", NULL, "I2S Enable" },
-};
+static int wov_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
-/*
- * Read maximum gain from device property and set it to mixer control.
- */
-static int cros_ec_set_gain_range(struct device *dev)
+ mutex_lock(&priv->wov_dma_lock);
+ priv->wov_substream = substream;
+ priv->wov_rp = priv->wov_wp = 0;
+ priv->wov_dma_offset = 0;
+ priv->wov_burst_read = true;
+ mutex_unlock(&priv->wov_dma_lock);
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int wov_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct soc_mixer_control *control;
- struct cros_ec_codec_data *codec_data = dev_get_drvdata(dev);
- int rc;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
- rc = device_property_read_u32(dev, "max-dmic-gain",
- &codec_data->max_dmic_gain);
- if (rc)
- return rc;
+ mutex_lock(&priv->wov_dma_lock);
+ wov_queue_dequeue(priv, wov_queue_size(priv));
+ priv->wov_substream = NULL;
+ mutex_unlock(&priv->wov_dma_lock);
- control = (struct soc_mixer_control *)
- mic_gain_control.private_value;
- control->max = codec_data->max_dmic_gain;
- control->platform_max = codec_data->max_dmic_gain;
+ cancel_delayed_work_sync(&priv->wov_copy_work);
- return 0;
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
}
-static int cros_ec_codec_probe(struct snd_soc_component *component)
+static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- int rc;
-
- struct cros_ec_codec_data *codec_data =
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct cros_ec_codec_priv *priv =
snd_soc_component_get_drvdata(component);
- rc = cros_ec_set_gain_range(codec_data->dev);
- if (rc)
- return rc;
+ return bytes_to_frames(runtime, priv->wov_dma_offset);
+}
- return snd_soc_add_component_controls(component, &mic_gain_control, 1);
+static struct page *wov_pcm_page(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return snd_pcm_lib_get_vmalloc_page(substream, offset);
}
-static const struct snd_soc_component_driver cros_ec_component_driver = {
- .probe = cros_ec_codec_probe,
- .dapm_widgets = cros_ec_codec_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cros_ec_codec_dapm_widgets),
- .dapm_routes = cros_ec_codec_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(cros_ec_codec_dapm_routes),
+static const struct snd_soc_component_driver wov_component_driver = {
+ .probe = wov_probe,
+ .remove = wov_remove,
+ .controls = wov_controls,
+ .num_controls = ARRAY_SIZE(wov_controls),
+ .open = wov_pcm_open,
+ .hw_params = wov_pcm_hw_params,
+ .hw_free = wov_pcm_hw_free,
+ .pointer = wov_pcm_pointer,
+ .page = wov_pcm_page,
};
-/*
- * Platform device and platform driver fro cros-ec-codec.
- */
-static int cros_ec_codec_platform_probe(struct platform_device *pd)
+static int cros_ec_codec_platform_probe(struct platform_device *pdev)
{
- struct device *dev = &pd->dev;
- struct cros_ec_device *ec_device = dev_get_drvdata(pd->dev.parent);
- struct cros_ec_codec_data *codec_data;
+ struct device *dev = &pdev->dev;
+ struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent);
+ struct cros_ec_codec_priv *priv;
+ struct ec_param_ec_codec p;
+ struct ec_response_ec_codec_get_capabilities r;
+ int ret;
+#ifdef CONFIG_OF
+ struct device_node *node;
+ struct resource res;
+ u64 ec_shm_size;
+ const __be32 *regaddr_p;
+#endif
- codec_data = devm_kzalloc(dev, sizeof(struct cros_ec_codec_data),
- GFP_KERNEL);
- if (!codec_data)
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
- codec_data->dev = dev;
- codec_data->ec_device = ec_device;
+#ifdef CONFIG_OF
+ regaddr_p = of_get_address(dev->of_node, 0, &ec_shm_size, NULL);
+ if (regaddr_p) {
+ priv->ec_shm_addr = of_read_number(regaddr_p, 2);
+ priv->ec_shm_len = ec_shm_size;
- platform_set_drvdata(pd, codec_data);
+ dev_dbg(dev, "ec_shm_addr=%#llx len=%#x\n",
+ priv->ec_shm_addr, priv->ec_shm_len);
+ }
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (node) {
+ ret = of_address_to_resource(node, 0, &res);
+ if (!ret) {
+ priv->ap_shm_phys_addr = res.start;
+ priv->ap_shm_len = resource_size(&res);
+ priv->ap_shm_addr =
+ (uint64_t)(uintptr_t)devm_ioremap_wc(
+ dev, priv->ap_shm_phys_addr,
+ priv->ap_shm_len);
+ priv->ap_shm_last_alloc = priv->ap_shm_phys_addr;
+
+ dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n",
+ priv->ap_shm_phys_addr, priv->ap_shm_len);
+ }
+ }
+#endif
+
+ priv->dev = dev;
+ priv->ec_device = ec_device;
+ atomic_set(&priv->dmic_probed, 0);
+
+ p.cmd = EC_CODEC_GET_CAPABILITIES;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret) {
+ dev_err(dev, "failed to EC_CODEC_GET_CAPABILITIES\n");
+ return ret;
+ }
+ priv->ec_capabilities = r.capabilities;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = devm_snd_soc_register_component(dev, &i2s_rx_component_driver,
+ &i2s_rx_dai_driver, 1);
+ if (ret)
+ return ret;
- return devm_snd_soc_register_component(dev, &cros_ec_component_driver,
- cros_ec_dai, ARRAY_SIZE(cros_ec_dai));
+ return devm_snd_soc_register_component(dev, &wov_component_driver,
+ &wov_dai_driver, 1);
}
#ifdef CONFIG_OF
@@ -427,7 +1049,7 @@ MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
static struct platform_driver cros_ec_codec_platform_driver = {
.driver = {
- .name = DRV_NAME,
+ .name = "cros-ec-codec",
.of_match_table = of_match_ptr(cros_ec_codec_of_match),
},
.probe = cros_ec_codec_platform_probe,
@@ -438,4 +1060,4 @@ module_platform_driver(cros_ec_codec_platform_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ChromeOS EC codec driver");
MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>");
-MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_ALIAS("platform:cros-ec-codec");
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
index 1c1ba7bea4d8..2ad00ed21bec 100644
--- a/sound/soc/codecs/cx2072x.c
+++ b/sound/soc/codecs/cx2072x.c
@@ -1507,7 +1507,7 @@ static int cx2072x_probe(struct snd_soc_component *codec)
regmap_multi_reg_write(cx2072x->regmap, cx2072x_reg_init,
ARRAY_SIZE(cx2072x_reg_init));
- /* configre PortC as input device */
+ /* configure PortC as input device */
regmap_update_bits(cx2072x->regmap, CX2072X_PORTC_PIN_CTRL,
0x20, 0x20);
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index 4570f662fb48..6803d39e09a5 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -14,13 +14,11 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/hdaudio_ext.h>
+#include <sound/hda_i915.h>
#include <sound/hda_codec.h>
#include <sound/hda_register.h>
-#include "hdac_hda.h"
-#define HDAC_ANALOG_DAI_ID 0
-#define HDAC_DIGITAL_DAI_ID 1
-#define HDAC_ALT_ANALOG_DAI_ID 2
+#include "hdac_hda.h"
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_U8 | \
@@ -32,6 +30,11 @@
SNDRV_PCM_FMTBIT_U32_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+#define STUB_HDMI_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)
+
static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
@@ -121,7 +124,46 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = {
.formats = STUB_FORMATS,
.sig_bits = 24,
},
-}
+},
+{
+ .id = HDAC_HDMI_0_DAI_ID,
+ .name = "intel-hdmi-hifi1",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi1",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_HDMI_1_DAI_ID,
+ .name = "intel-hdmi-hifi2",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi2",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_HDMI_2_DAI_ID,
+ .name = "intel-hdmi-hifi3",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi3",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
};
@@ -135,10 +177,11 @@ static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
hda_pvt = snd_soc_component_get_drvdata(component);
pcm = &hda_pvt->pcm[dai->id];
+
if (tx_mask)
- pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
+ pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
else
- pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
+ pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
return 0;
}
@@ -278,6 +321,12 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
struct hda_pcm *cpcm;
const char *pcm_name;
+ /*
+ * map DAI ID to the closest matching PCM name, using the naming
+ * scheme used by hda-codec snd_hda_gen_build_pcms() and for
+ * HDMI in hda_codec patch_hdmi.c)
+ */
+
switch (dai->id) {
case HDAC_ANALOG_DAI_ID:
pcm_name = "Analog";
@@ -288,13 +337,22 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
case HDAC_ALT_ANALOG_DAI_ID:
pcm_name = "Alt Analog";
break;
+ case HDAC_HDMI_0_DAI_ID:
+ pcm_name = "HDMI 0";
+ break;
+ case HDAC_HDMI_1_DAI_ID:
+ pcm_name = "HDMI 1";
+ break;
+ case HDAC_HDMI_2_DAI_ID:
+ pcm_name = "HDMI 2";
+ break;
default:
dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id);
return NULL;
}
list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
- if (strpbrk(cpcm->name, pcm_name))
+ if (strstr(cpcm->name, pcm_name))
return cpcm;
}
@@ -302,6 +360,18 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
return NULL;
}
+static bool is_hdmi_codec(struct hda_codec *hcodec)
+{
+ struct hda_pcm *cpcm;
+
+ list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
+ if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI)
+ return true;
+ }
+
+ return false;
+}
+
static int hdac_hda_codec_probe(struct snd_soc_component *component)
{
struct hdac_hda_priv *hda_pvt =
@@ -322,6 +392,15 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+ /*
+ * Ensure any HDA display is powered at codec probe.
+ * After snd_hda_codec_device_new(), display power is
+ * managed by runtime PM.
+ */
+ if (hda_pvt->need_display_power)
+ snd_hdac_display_power(hdev->bus,
+ HDA_CODEC_IDX_CONTROLLER, true);
+
ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card,
hdev->addr, hcodec);
if (ret < 0) {
@@ -366,20 +445,31 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
dev_dbg(&hdev->dev, "no patch file found\n");
}
+ /* configure codec for 1:1 PCM:DAI mapping */
+ hcodec->mst_no_extra_pcms = 1;
+
ret = snd_hda_codec_parse_pcms(hcodec);
if (ret < 0) {
dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
goto error;
}
- ret = snd_hda_codec_build_controls(hcodec);
- if (ret < 0) {
- dev_err(&hdev->dev, "unable to create controls %d\n", ret);
- goto error;
+ /* HDMI controls need to be created in machine drivers */
+ if (!is_hdmi_codec(hcodec)) {
+ ret = snd_hda_codec_build_controls(hcodec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "unable to create controls %d\n",
+ ret);
+ goto error;
+ }
}
hcodec->core.lazy_cache = true;
+ if (hda_pvt->need_display_power)
+ snd_hdac_display_power(hdev->bus,
+ HDA_CODEC_IDX_CONTROLLER, false);
+
/*
* hdac_device core already sets the state to active and calls
* get_noresume. So enable runtime and set the device to suspend.
diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h
index 6b1bd4f428e7..e145cec085b8 100644
--- a/sound/soc/codecs/hdac_hda.h
+++ b/sound/soc/codecs/hdac_hda.h
@@ -6,6 +6,16 @@
#ifndef __HDAC_HDA_H__
#define __HDAC_HDA_H__
+enum {
+ HDAC_ANALOG_DAI_ID = 0,
+ HDAC_DIGITAL_DAI_ID,
+ HDAC_ALT_ANALOG_DAI_ID,
+ HDAC_HDMI_0_DAI_ID,
+ HDAC_HDMI_1_DAI_ID,
+ HDAC_HDMI_2_DAI_ID,
+ HDAC_LAST_DAI_ID = HDAC_HDMI_2_DAI_ID,
+};
+
struct hdac_hda_pcm {
int stream_tag[2];
unsigned int format_val[2];
@@ -13,7 +23,8 @@ struct hdac_hda_pcm {
struct hdac_hda_priv {
struct hda_codec codec;
- struct hdac_hda_pcm pcm[2];
+ struct hdac_hda_pcm pcm[HDAC_LAST_DAI_ID];
+ bool need_display_power;
};
#define hdac_to_hda_priv(_hdac) \
diff --git a/sound/soc/codecs/madera.h b/sound/soc/codecs/madera.h
index 1f3e8e230cf2..6d8938a3fb64 100644
--- a/sound/soc/codecs/madera.h
+++ b/sound/soc/codecs/madera.h
@@ -27,6 +27,7 @@
#define MADERA_FLL_SRC_NONE -1
#define MADERA_FLL_SRC_MCLK1 0
#define MADERA_FLL_SRC_MCLK2 1
+#define MADERA_FLL_SRC_MCLK3 2
#define MADERA_FLL_SRC_SLIMCLK 3
#define MADERA_FLL_SRC_FLL1 4
#define MADERA_FLL_SRC_FLL2 5
@@ -51,6 +52,7 @@
#define MADERA_CLK_SRC_MCLK1 0x0
#define MADERA_CLK_SRC_MCLK2 0x1
+#define MADERA_CLK_SRC_MCLK3 0x2
#define MADERA_CLK_SRC_FLL1 0x4
#define MADERA_CLK_SRC_FLL2 0x5
#define MADERA_CLK_SRC_FLL3 0x6
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index e3d311fb510e..f53235be77d9 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -228,6 +228,10 @@
#define CDC_A_RX_EAR_CTL (0xf19E)
#define RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK BIT(0)
#define RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE BIT(0)
+#define RX_EAR_CTL_PA_EAR_PA_EN_MASK BIT(6)
+#define RX_EAR_CTL_PA_EAR_PA_EN_ENABLE BIT(6)
+#define RX_EAR_CTL_PA_SEL_MASK BIT(7)
+#define RX_EAR_CTL_PA_SEL BIT(7)
#define CDC_A_SPKR_DAC_CTL (0xf1B0)
#define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4)
@@ -312,6 +316,7 @@ static const char *const hph_text[] = { "ZERO", "Switch", };
static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT(
ARRAY_SIZE(hph_text), hph_text);
+static const struct snd_kcontrol_new ear_mux = SOC_DAPM_ENUM("EAR_S", hph_enum);
static const struct snd_kcontrol_new hphl_mux = SOC_DAPM_ENUM("HPHL", hph_enum);
static const struct snd_kcontrol_new hphr_mux = SOC_DAPM_ENUM("HPHR", hph_enum);
@@ -685,6 +690,34 @@ static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w,
return 0;
}
+static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_SEL_MASK, RX_EAR_CTL_PA_SEL);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_EAR_PA_EN_MASK,
+ RX_EAR_CTL_PA_EAR_PA_EN_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_EAR_PA_EN_MASK, 0);
+ /* Delay to reduce ear turn off pop */
+ usleep_range(7000, 7100);
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_SEL_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
static const struct reg_default wcd_reg_defaults_2_0[] = {
{CDC_A_RX_COM_OCP_CTL, 0xD1},
{CDC_A_RX_COM_OCP_COUNT, 0xFF},
@@ -801,12 +834,20 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = {
{"PDM_TX", NULL, "A_MCLK2"},
{"A_MCLK2", NULL, "A_MCLK"},
+ /* Earpiece (RX MIX1) */
+ {"EAR", NULL, "EAR_S"},
+ {"EAR_S", "Switch", "EAR PA"},
+ {"EAR PA", NULL, "RX_BIAS"},
+ {"EAR PA", NULL, "HPHL DAC"},
+ {"EAR PA", NULL, "HPHR DAC"},
+ {"EAR PA", NULL, "EAR CP"},
+
/* Headset (RX MIX1 and RX MIX2) */
{"HEADPHONE", NULL, "HPHL PA"},
{"HEADPHONE", NULL, "HPHR PA"},
- {"HPHL PA", NULL, "EAR_HPHL_CLK"},
- {"HPHR PA", NULL, "EAR_HPHR_CLK"},
+ {"HPHL DAC", NULL, "EAR_HPHL_CLK"},
+ {"HPHR DAC", NULL, "EAR_HPHR_CLK"},
{"CP", NULL, "NCP_CLK"},
@@ -847,11 +888,20 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AMIC1"),
SND_SOC_DAPM_INPUT("AMIC3"),
SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
/* RX stuff */
SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM,
+ 0, 0, NULL, 0,
+ pm8916_wcd_analog_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux),
+ SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0),
+
SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0),
SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux),
SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL,
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c
index bb737fd678cc..1b830ea4f6ed 100644
--- a/sound/soc/codecs/mt6358.c
+++ b/sound/soc/codecs/mt6358.c
@@ -93,6 +93,8 @@ struct mt6358_priv {
int mtkaif_protocol;
struct regulator *avdd_reg;
+
+ int wov_enabled;
};
int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
@@ -464,6 +466,106 @@ static int mt6358_put_volsw(struct snd_kcontrol *kcontrol,
return ret;
}
+static void mt6358_restore_pga(struct mt6358_priv *priv);
+
+static int mt6358_enable_wov_phase2(struct mt6358_priv *priv)
+{
+ /* analog */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xffff, 0x0800);
+ mt6358_restore_pga(priv);
+
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9929);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xffff, 0x0025);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8,
+ 0xffff, 0x0005);
+
+ /* digital */
+ regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x0120);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0xffff);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0200);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2424);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xdbac);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x029e);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0,
+ 0xffff, 0x0451);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0x68d1);
+
+ return 0;
+}
+
+static int mt6358_disable_wov_phase2(struct mt6358_priv *priv)
+{
+ /* digital */
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0xc000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0,
+ 0xffff, 0x0450);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0,
+ 0xffff, 0x0c00);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0100);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x006c);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xa879);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2323);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0400);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x02d8);
+ regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
+ 0xffff, 0x0000);
+
+ /* analog */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8,
+ 0xffff, 0x0004);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9829);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xffff, 0x0000);
+ mt6358_restore_pga(priv);
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5);
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0xffff, 0x0010);
+
+ return 0;
+}
+
+static int mt6358_get_wov(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = priv->wov_enabled;
+ return 0;
+}
+
+static int mt6358_put_wov(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
+ int enabled = ucontrol->value.integer.value[0];
+
+ if (priv->wov_enabled != enabled) {
+ if (enabled)
+ mt6358_enable_wov_phase2(priv);
+ else
+ mt6358_disable_wov_phase2(priv);
+
+ priv->wov_enabled = enabled;
+ }
+
+ return 0;
+}
+
static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0);
@@ -483,6 +585,9 @@ static const struct snd_kcontrol_new mt6358_snd_controls[] = {
MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1,
8, 4, 0,
snd_soc_get_volsw, mt6358_put_volsw, pga_tlv),
+
+ SOC_SINGLE_BOOL_EXT("Wake-on-Voice Phase2 Switch", 0,
+ mt6358_get_wov, mt6358_put_wov),
};
/* MUX */
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index 88b75695fbf7..9711fab296eb 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -9,7 +9,9 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -59,9 +61,11 @@ struct pcm3168a_priv {
struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
struct regmap *regmap;
struct clk *scki;
+ struct gpio_desc *gpio_rst;
unsigned long sysclk;
struct pcm3168a_io_params io_params[2];
+ struct snd_soc_dai_driver dai_drv[2];
};
static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
@@ -314,6 +318,34 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
return 0;
}
+static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ u64 formats = SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE;
+ unsigned int channel_max = dai->id == PCM3168A_DAI_DAC ? 8 : 6;
+
+ if (pcm3168a->io_params[dai->id].fmt == PCM3168A_FMT_RIGHT_J) {
+ /* S16_LE is only supported in RIGHT_J mode */
+ formats |= SNDRV_PCM_FMTBIT_S16_LE;
+
+ /*
+ * If multi DIN/DOUT is not selected, RIGHT_J can only support
+ * two channels (no TDM support)
+ */
+ if (pcm3168a->io_params[dai->id].tdm_slots != 2)
+ channel_max = 2;
+ }
+
+ if (dai->id == PCM3168A_DAI_DAC) {
+ dai->driver->playback.channels_max = channel_max;
+ dai->driver->playback.formats = formats;
+ } else {
+ dai->driver->capture.channels_max = channel_max;
+ dai->driver->capture.formats = formats;
+ }
+}
+
static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
{
struct snd_soc_component *component = dai->component;
@@ -376,6 +408,8 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+ pcm3168a_update_fixup_pcm_stream(dai);
+
return 0;
}
@@ -409,6 +443,8 @@ static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
else
io_params->tdm_mask = rx_mask;
+ pcm3168a_update_fixup_pcm_stream(dai);
+
return 0;
}
@@ -530,63 +566,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int pcm3168a_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_component *component = dai->component;
- struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
- unsigned int sample_min;
- unsigned int channel_max;
- unsigned int channel_maxs[] = {
- 8, /* DAC */
- 6 /* ADC */
- };
-
- /*
- * Available Data Bits
- *
- * RIGHT_J : 24 / 16
- * LEFT_J : 24
- * I2S : 24
- *
- * TDM available
- *
- * I2S
- * LEFT_J
- */
- switch (pcm3168a->io_params[dai->id].fmt) {
- case PCM3168A_FMT_RIGHT_J:
- sample_min = 16;
- channel_max = 2;
- break;
- case PCM3168A_FMT_LEFT_J:
- case PCM3168A_FMT_I2S:
- case PCM3168A_FMT_DSP_A:
- case PCM3168A_FMT_DSP_B:
- sample_min = 24;
- channel_max = channel_maxs[dai->id];
- break;
- default:
- sample_min = 24;
- channel_max = 2;
- }
-
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- sample_min, 32);
-
- /* Allow all channels in multi DIN/DOUT mode */
- if (pcm3168a->io_params[dai->id].tdm_slots == 2)
- channel_max = channel_maxs[dai->id];
-
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- 2, channel_max);
-
- return 0;
-}
static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
- .startup = pcm3168a_startup,
.set_fmt = pcm3168a_set_dai_fmt,
.set_sysclk = pcm3168a_set_dai_sysclk,
.hw_params = pcm3168a_hw_params,
@@ -666,6 +646,7 @@ static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case PCM3168A_RST_SMODE:
case PCM3168A_DAC_ZERO:
case PCM3168A_ADC_OV:
return true;
@@ -725,6 +706,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, pcm3168a);
+ /*
+ * Request the reset (connected to RST pin) gpio line as non exclusive
+ * as the same reset line might be connected to multiple pcm3168a codec
+ *
+ * The RST is low active, we want the GPIO line to be high initially, so
+ * request the initial level to LOW which in practice means DEASSERTED:
+ * The deasserted level of GPIO_ACTIVE_LOW is HIGH.
+ */
+ pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW |
+ GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+ if (IS_ERR(pcm3168a->gpio_rst)) {
+ ret = PTR_ERR(pcm3168a->gpio_rst);
+ if (ret != -EPROBE_DEFER )
+ dev_err(dev, "failed to acquire RST gpio: %d\n", ret);
+
+ return ret;
+ }
+
pcm3168a->scki = devm_clk_get(dev, "scki");
if (IS_ERR(pcm3168a->scki)) {
ret = PTR_ERR(pcm3168a->scki);
@@ -766,18 +766,28 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
goto err_regulator;
}
- ret = pcm3168a_reset(pcm3168a);
- if (ret) {
- dev_err(dev, "Failed to reset device: %d\n", ret);
- goto err_regulator;
+ if (pcm3168a->gpio_rst) {
+ /*
+ * The device is taken out from reset via GPIO line, wait for
+ * 3846 SCKI clock cycles for the internal reset de-assertion
+ */
+ msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
+ } else {
+ ret = pcm3168a_reset(pcm3168a);
+ if (ret) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_regulator;
+ }
}
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_idle(dev);
- ret = devm_snd_soc_register_component(dev, &pcm3168a_driver, pcm3168a_dais,
- ARRAY_SIZE(pcm3168a_dais));
+ memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv));
+ ret = devm_snd_soc_register_component(dev, &pcm3168a_driver,
+ pcm3168a->dai_drv,
+ ARRAY_SIZE(pcm3168a->dai_drv));
if (ret) {
dev_err(dev, "failed to register component: %d\n", ret);
goto err_regulator;
@@ -806,6 +816,15 @@ static void pcm3168a_disable(struct device *dev)
void pcm3168a_remove(struct device *dev)
{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+ /*
+ * The RST is low active, we want the GPIO line to be low when the
+ * driver is removed, so set level to 1 which in practice means
+ * ASSERTED:
+ * The asserted level of GPIO_ACTIVE_LOW is LOW.
+ */
+ gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1);
pm_runtime_disable(dev);
#ifndef CONFIG_PM
pcm3168a_disable(dev);
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
index be1e276e3631..2552073e54ce 100644
--- a/sound/soc/codecs/rt1011.c
+++ b/sound/soc/codecs/rt1011.c
@@ -61,7 +61,6 @@ static const struct reg_sequence init_list[] = {
{ RT1011_DAC_SET_1, 0xe702 },
{ RT1011_DAC_SET_3, 0x2004 },
};
-#define RT1011_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt1011_reg[] = {
{0x0000, 0x0000},
@@ -684,7 +683,8 @@ static int rt1011_reg_init(struct snd_soc_component *component)
{
struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
- regmap_multi_reg_write(rt1011->regmap, init_list, RT1011_INIT_REG_LEN);
+ regmap_multi_reg_write(rt1011->regmap,
+ init_list, ARRAY_SIZE(init_list));
return 0;
}
@@ -989,7 +989,7 @@ static SOC_ENUM_SINGLE_DECL(rt1011_din_source_enum, RT1011_CROSS_BQ_SET_1, 5,
static const char * const rt1011_tdm_data_out_select[] = {
"TDM_O_LR", "BQ1", "DVOL", "BQ10", "ALC", "DMIX", "ADC_SRC_LR",
- "ADC_O_LR", "ADC_MONO", "RSPK_BPF_LR", "DMIX_ADD", "ENVELOPE_FS",
+ "ADC_O_LR", "ADC_MONO", "RSPK_BPF_LR", "DMIX_ADD", "ENVELOPE_FS",
"SEP_O_GAIN", "ALC_BK_GAIN", "STP_V_C", "DMIX_ABST"
};
@@ -1002,7 +1002,7 @@ static SOC_ENUM_SINGLE_DECL(rt1011_tdm2_l_dac1_enum, RT1011_TDM2_SET_4, 12,
rt1011_tdm_l_ch_data_select);
static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_dat_enum,
- RT1011_ADCDAT_OUT_SOURCE, 0, rt1011_tdm_data_out_select);
+ RT1011_ADCDAT_OUT_SOURCE, 0, rt1011_tdm_data_out_select);
static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_loc_enum, RT1011_TDM1_SET_2, 0,
rt1011_tdm_l_ch_data_select);
@@ -1024,9 +1024,9 @@ static const char * const rt1011_tdm_adc_swap_select[] = {
"L/R", "R/L", "L/L", "R/R"
};
-static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc1_1_enum, RT1011_TDM1_SET_3, 6,
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc1_1_enum, RT1011_TDM1_SET_3, 6,
rt1011_tdm_adc_swap_select);
-static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc2_1_enum, RT1011_TDM1_SET_3, 4,
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc2_1_enum, RT1011_TDM1_SET_3, 4,
rt1011_tdm_adc_swap_select);
static void rt1011_reset(struct regmap *regmap)
@@ -1092,9 +1092,9 @@ static bool rt1011_validate_bq_drc_coeff(unsigned short reg)
{
if ((reg == RT1011_DAC_SET_1) |
(reg >= RT1011_ADC_SET && reg <= RT1011_ADC_SET_1) |
- (reg == RT1011_ADC_SET_4) | (reg == RT1011_ADC_SET_5) |
+ (reg == RT1011_ADC_SET_4) | (reg == RT1011_ADC_SET_5) |
(reg == RT1011_MIXER_1) |
- (reg == RT1011_A_TIMING_1) | (reg >= RT1011_POWER_7 &&
+ (reg == RT1011_A_TIMING_1) | (reg >= RT1011_POWER_7 &&
reg <= RT1011_POWER_8) |
(reg == RT1011_CLASS_D_POS) | (reg == RT1011_ANALOG_CTRL) |
(reg >= RT1011_SPK_TEMP_PROTECT_0 &&
@@ -1163,9 +1163,6 @@ static int rt1011_bq_drc_coeff_put(struct snd_kcontrol *kcontrol,
(struct rt1011_bq_drc_params *)ucontrol->value.integer.value;
unsigned int i, mode_idx = 0;
- if (!component->card->instantiated)
- return 0;
-
if (strstr(ucontrol->id.name, "AdvanceMode Initial Set"))
mode_idx = RT1011_ADVMODE_INITIAL_SET;
else if (strstr(ucontrol->id.name, "AdvanceMode SEP BQ Coeff"))
@@ -1236,9 +1233,6 @@ static int rt1011_r0_cali_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
- if (!component->card->instantiated)
- return 0;
-
rt1011->cali_done = 0;
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF &&
ucontrol->value.integer.value[0])
@@ -1284,9 +1278,6 @@ static int rt1011_r0_load_mode_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.integer.value[0] == rt1011->r0_reg)
return 0;
- if (!component->card->instantiated)
- return 0;
-
if (ucontrol->value.integer.value[0] == 0)
return -EINVAL;
@@ -1298,7 +1289,7 @@ static int rt1011_r0_load_mode_put(struct snd_kcontrol *kcontrol,
r0_integer = format / rt1011->r0_reg / 128;
r0_factor = ((format / rt1011->r0_reg * 100) / 128)
- (r0_integer * 100);
- dev_info(dev, "New r0 resistance about %d.%02d ohm, reg=0x%X\n",
+ dev_info(dev, "New r0 resistance about %d.%02d ohm, reg=0x%X\n",
r0_integer, r0_factor, rt1011->r0_reg);
if (rt1011->r0_reg)
@@ -1640,6 +1631,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
default:
ret = -EINVAL;
+ goto _set_fmt_err_;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -1650,6 +1642,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
default:
ret = -EINVAL;
+ goto _set_fmt_err_;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -1666,6 +1659,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
default:
ret = -EINVAL;
+ goto _set_fmt_err_;
}
switch (dai->id) {
@@ -1683,6 +1677,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
ret = -EINVAL;
}
+_set_fmt_err_:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
@@ -1778,7 +1773,8 @@ static int rt1011_set_component_pll(struct snd_soc_component *component,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ freq_in);
return ret;
}
@@ -1805,8 +1801,8 @@ static int rt1011_set_tdm_slot(struct snd_soc_dai *dai,
struct snd_soc_component *component = dai->component;
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(component);
- unsigned int val = 0, tdm_en = 0;
- int ret = 0;
+ unsigned int val = 0, tdm_en = 0, rx_slotnum, tx_slotnum;
+ int ret = 0, first_bit, last_bit;
snd_soc_dapm_mutex_lock(dapm);
if (rx_mask || tx_mask)
@@ -1829,6 +1825,7 @@ static int rt1011_set_tdm_slot(struct snd_soc_dai *dai,
break;
default:
ret = -EINVAL;
+ goto _set_tdm_err_;
}
switch (slot_width) {
@@ -1848,22 +1845,153 @@ static int rt1011_set_tdm_slot(struct snd_soc_dai *dai,
break;
default:
ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Rx slot configuration */
+ rx_slotnum = hweight_long(rx_mask);
+ first_bit = find_next_bit((unsigned long *)&rx_mask, 32, 0);
+ if (rx_slotnum > 1 || rx_slotnum == 0) {
+ ret = -EINVAL;
+ dev_dbg(component->dev, "too many rx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK,
+ RT1011_MONO_L_CHANNEL);
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_4,
+ RT1011_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1011_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit+1) << RT1011_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK,
+ RT1011_MONO_R_CHANNEL);
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_4,
+ RT1011_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1011_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit-1) << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1011_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Tx slot configuration */
+ tx_slotnum = hweight_long(tx_mask);
+ first_bit = find_next_bit((unsigned long *)&tx_mask, 32, 0);
+ last_bit = find_last_bit((unsigned long *)&tx_mask, 32);
+ if (tx_slotnum > 2 || (last_bit-first_bit) > 1) {
+ ret = -EINVAL;
+ dev_dbg(component->dev, "too many tx slots or tx slot location error\n");
+ goto _set_tdm_err_;
+ }
+
+ if (tx_slotnum == 1) {
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_2,
+ RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK |
+ RT1011_TDM_ADCDAT1_DATA_LOCATION, first_bit);
+ switch (first_bit) {
+ case 1:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC1_1_MASK,
+ RT1011_TDM_I2S_RX_ADC1_1_LL);
+ break;
+ case 3:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC2_1_MASK,
+ RT1011_TDM_I2S_RX_ADC2_1_LL);
+ break;
+ case 5:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC3_1_MASK,
+ RT1011_TDM_I2S_RX_ADC3_1_LL);
+ break;
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC4_1_MASK,
+ RT1011_TDM_I2S_RX_ADC4_1_LL);
+ break;
+ case 0:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC1_1_MASK, 0);
+ break;
+ case 2:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC2_1_MASK, 0);
+ break;
+ case 4:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC3_1_MASK, 0);
+ break;
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC4_1_MASK, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_dbg(component->dev,
+ "tx slot location error\n");
+ goto _set_tdm_err_;
+ }
+ } else if (tx_slotnum == 2) {
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_2,
+ RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK |
+ RT1011_TDM_ADCDAT1_DATA_LOCATION,
+ RT1011_TDM_I2S_DOCK_ADCDAT_2CH | first_bit);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_dbg(component->dev,
+ "tx slot location should be paired and start from slot0/2/4/6\n");
+ goto _set_tdm_err_;
+ }
}
snd_soc_component_update_bits(component, RT1011_TDM1_SET_1,
RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK |
- RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
+ RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
snd_soc_component_update_bits(component, RT1011_TDM2_SET_1,
RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK |
- RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
+ RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
snd_soc_component_update_bits(component, RT1011_TDM1_SET_2,
- RT1011_TDM_I2S_DOCK_EN_1_MASK, tdm_en);
+ RT1011_TDM_I2S_DOCK_EN_1_MASK, tdm_en);
snd_soc_component_update_bits(component, RT1011_TDM2_SET_2,
- RT1011_TDM_I2S_DOCK_EN_2_MASK, tdm_en);
- snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
- RT1011_ADCDAT1_PIN_CONFIG | RT1011_ADCDAT2_PIN_CONFIG,
- RT1011_ADCDAT1_OUTPUT | RT1011_ADCDAT2_OUTPUT);
+ RT1011_TDM_I2S_DOCK_EN_2_MASK, tdm_en);
+ if (tx_slotnum)
+ snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
+ RT1011_ADCDAT1_PIN_CONFIG | RT1011_ADCDAT2_PIN_CONFIG,
+ RT1011_ADCDAT1_OUTPUT | RT1011_ADCDAT2_OUTPUT);
+_set_tdm_err_:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
@@ -1982,7 +2110,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt1011 = {
.remove = rt1011_remove,
.suspend = rt1011_suspend,
.resume = rt1011_resume,
- .set_bias_level = rt1011_set_bias_level,
+ .set_bias_level = rt1011_set_bias_level,
.controls = rt1011_snd_controls,
.num_controls = ARRAY_SIZE(rt1011_snd_controls),
.dapm_widgets = rt1011_dapm_widgets,
@@ -1991,9 +2119,9 @@ static const struct snd_soc_component_driver soc_component_dev_rt1011 = {
.num_dapm_routes = ARRAY_SIZE(rt1011_dapm_routes),
.set_sysclk = rt1011_set_component_sysclk,
.set_pll = rt1011_set_component_pll,
- .use_pmdown_time = 1,
- .endianness = 1,
- .non_legacy_dai_naming = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
};
static const struct regmap_config rt1011_regmap = {
@@ -2095,17 +2223,17 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
dc_offset = value << 16;
regmap_read(rt1011->regmap, RT1011_EFUSE_ADC_OFFSET_15_0, &value);
dc_offset |= (value & 0xffff);
- dev_info(dev, "ADC offset=0x%x\n", dc_offset);
+ dev_info(dev, "ADC offset=0x%x\n", dc_offset);
regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_20_16, &value);
dc_offset = value << 16;
regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_15_0, &value);
dc_offset |= (value & 0xffff);
- dev_info(dev, "Gain0 offset=0x%x\n", dc_offset);
+ dev_info(dev, "Gain0 offset=0x%x\n", dc_offset);
regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_20_16, &value);
dc_offset = value << 16;
regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_15_0, &value);
dc_offset |= (value & 0xffff);
- dev_info(dev, "Gain1 offset=0x%x\n", dc_offset);
+ dev_info(dev, "Gain1 offset=0x%x\n", dc_offset);
if (cali_flag) {
@@ -2125,7 +2253,7 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
while (count < chk_cnt) {
msleep(100);
regmap_read(rt1011->regmap,
- RT1011_INIT_RECIPROCAL_SYN_24_16, &value);
+ RT1011_INIT_RECIPROCAL_SYN_24_16, &value);
r0[count%3] = value << 16;
regmap_read(rt1011->regmap,
RT1011_INIT_RECIPROCAL_SYN_15_0, &value);
@@ -2140,7 +2268,7 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
break;
}
if (count > chk_cnt) {
- dev_err(dev, "Calibrate R0 Failure\n");
+ dev_err(dev, "Calibrate R0 Failure\n");
ret = -EAGAIN;
} else {
format = 2147483648U; /* 2^24 * 128 */
@@ -2149,7 +2277,7 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
- (r0_integer * 100);
rt1011->r0_reg = r0[0];
rt1011->cali_done = 1;
- dev_info(dev, "r0 resistance about %d.%02d ohm, reg=0x%X\n",
+ dev_info(dev, "r0 resistance about %d.%02d ohm, reg=0x%X\n",
r0_integer, r0_factor, r0[0]);
}
}
@@ -2196,8 +2324,12 @@ static void rt1011_calibration_work(struct work_struct *work)
struct rt1011_priv *rt1011 =
container_of(work, struct rt1011_priv, cali_work);
struct snd_soc_component *component = rt1011->component;
+ unsigned int r0_integer, r0_factor, format;
- rt1011_calibrate(rt1011, 1);
+ if (rt1011->r0_calib)
+ rt1011_calibrate(rt1011, 0);
+ else
+ rt1011_calibrate(rt1011, 1);
/*
* This flag should reset after booting.
@@ -2208,6 +2340,40 @@ static void rt1011_calibration_work(struct work_struct *work)
/* initial */
rt1011_reg_init(component);
+
+ /* Apply temperature and calibration data from device property */
+ if (rt1011->temperature_calib <= 0xff &&
+ rt1011->temperature_calib > 0) {
+ snd_soc_component_update_bits(component,
+ RT1011_STP_INITIAL_RESISTANCE_TEMP, 0x3ff,
+ (rt1011->temperature_calib << 2));
+ }
+
+ if (rt1011->r0_calib) {
+ rt1011->r0_reg = rt1011->r0_calib;
+
+ format = 2147483648U; /* 2^24 * 128 */
+ r0_integer = format / rt1011->r0_reg / 128;
+ r0_factor = ((format / rt1011->r0_reg * 100) / 128)
+ - (r0_integer * 100);
+ dev_info(component->dev, "DP r0 resistance about %d.%02d ohm, reg=0x%X\n",
+ r0_integer, r0_factor, rt1011->r0_reg);
+
+ rt1011_r0_load(rt1011);
+ }
+}
+
+static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,temperature_calib",
+ &rt1011->temperature_calib);
+ device_property_read_u32(dev, "realtek,r0_calib",
+ &rt1011->r0_calib);
+
+ dev_dbg(dev, "%s: r0_calib: 0x%x, temperature_calib: 0x%x",
+ __func__, rt1011->r0_calib, rt1011->temperature_calib);
+
+ return 0;
}
static int rt1011_i2c_probe(struct i2c_client *i2c,
@@ -2219,11 +2385,13 @@ static int rt1011_i2c_probe(struct i2c_client *i2c,
rt1011 = devm_kzalloc(&i2c->dev, sizeof(struct rt1011_priv),
GFP_KERNEL);
- if (rt1011 == NULL)
+ if (!rt1011)
return -ENOMEM;
i2c_set_clientdata(i2c, rt1011);
+ rt1011_parse_dp(rt1011, &i2c->dev);
+
rt1011->regmap = devm_regmap_init_i2c(i2c, &rt1011_regmap);
if (IS_ERR(rt1011->regmap)) {
ret = PTR_ERR(rt1011->regmap);
@@ -2254,7 +2422,6 @@ static void rt1011_i2c_shutdown(struct i2c_client *client)
rt1011_reset(rt1011->regmap);
}
-
static struct i2c_driver rt1011_i2c_driver = {
.driver = {
.name = "rt1011",
diff --git a/sound/soc/codecs/rt1011.h b/sound/soc/codecs/rt1011.h
index 2d65983f3d0f..68fadc15fa8c 100644
--- a/sound/soc/codecs/rt1011.h
+++ b/sound/soc/codecs/rt1011.h
@@ -460,6 +460,23 @@
#define RT1011_TDM_I2S_DOCK_EN_1_MASK (0x1 << 3)
#define RT1011_TDM_I2S_DOCK_EN_1_SFT 3
#define RT1011_TDM_I2S_DOCK_EN_1 (0x1 << 3)
+#define RT1011_TDM_ADCDAT1_DATA_LOCATION (0x7 << 0)
+
+/* TDM1 Setting-3 (0x0118) */
+#define RT1011_TDM_I2S_RX_ADC1_1_MASK (0x3 << 6)
+#define RT1011_TDM_I2S_RX_ADC2_1_MASK (0x3 << 4)
+#define RT1011_TDM_I2S_RX_ADC3_1_MASK (0x3 << 2)
+#define RT1011_TDM_I2S_RX_ADC4_1_MASK (0x3 << 0)
+#define RT1011_TDM_I2S_RX_ADC1_1_LL (0x2 << 6)
+#define RT1011_TDM_I2S_RX_ADC2_1_LL (0x2 << 4)
+#define RT1011_TDM_I2S_RX_ADC3_1_LL (0x2 << 2)
+#define RT1011_TDM_I2S_RX_ADC4_1_LL (0x2 << 0)
+
+/* TDM1 Setting-4 (0x011a) */
+#define RT1011_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12)
+#define RT1011_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8)
+#define RT1011_TDM_I2S_TX_L_DAC1_1_SFT 12
+#define RT1011_TDM_I2S_TX_R_DAC1_1_SFT 8
/* TDM2 Setting-2 (0x0120) */
#define RT1011_TDM_I2S_DOCK_ADCDAT_LEN_2_MASK (0x7 << 13)
@@ -585,6 +602,12 @@
#define RT1011_STP_T0_EN_BIT 6
#define RT1011_STP_T0_EN (0x1 << 6)
+/* Cross Biquad Setting-1 (0x0702) */
+#define RT1011_MONO_LR_SEL_MASK (0x3 << 5)
+#define RT1011_MONO_L_CHANNEL (0x0 << 5)
+#define RT1011_MONO_R_CHANNEL (0x1 << 5)
+#define RT1011_MONO_LR_MIX_CHANNEL (0x2 << 5)
+
/* ClassD Internal Setting-1 (0x1300) */
#define RT1011_DRIVER_READY_SPK (0x1 << 12)
#define RT1011_DRIVER_READY_SPK_BIT 12
@@ -667,6 +690,7 @@ struct rt1011_priv {
int bq_drc_set;
unsigned int r0_reg, cali_done;
+ unsigned int r0_calib, temperature_calib;
int recv_spk_mode;
};
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
index 892ea406a69b..57ff5aee452d 100644
--- a/sound/soc/codecs/rt5514-spi.c
+++ b/sound/soc/codecs/rt5514-spi.c
@@ -201,18 +201,18 @@ static irqreturn_t rt5514_spi_irq(int irq, void *data)
}
/* PCM for streaming audio from the DSP buffer */
-static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream)
+static int rt5514_spi_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware);
return 0;
}
-static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+static int rt5514_spi_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct rt5514_dsp *rt5514_dsp =
snd_soc_component_get_drvdata(component);
int ret;
@@ -234,10 +234,9 @@ static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
+static int rt5514_spi_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct rt5514_dsp *rt5514_dsp =
snd_soc_component_get_drvdata(component);
@@ -251,24 +250,22 @@ static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
}
static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
+ struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct rt5514_dsp *rt5514_dsp =
snd_soc_component_get_drvdata(component);
return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
}
-static const struct snd_pcm_ops rt5514_spi_pcm_ops = {
- .open = rt5514_spi_pcm_open,
- .hw_params = rt5514_spi_hw_params,
- .hw_free = rt5514_spi_hw_free,
- .pointer = rt5514_spi_pcm_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
-};
+static struct page *rt5514_spi_pcm_page(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return snd_pcm_lib_get_vmalloc_page(substream, offset);
+}
static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
{
@@ -302,9 +299,13 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
}
static const struct snd_soc_component_driver rt5514_spi_component = {
- .name = DRV_NAME,
- .probe = rt5514_spi_pcm_probe,
- .ops = &rt5514_spi_pcm_ops,
+ .name = DRV_NAME,
+ .probe = rt5514_spi_pcm_probe,
+ .open = rt5514_spi_pcm_open,
+ .hw_params = rt5514_spi_hw_params,
+ .hw_free = rt5514_spi_hw_free,
+ .pointer = rt5514_spi_pcm_pointer,
+ .page = rt5514_spi_pcm_page,
};
/**
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 19662ee330d6..92d67010aeed 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3639,6 +3639,12 @@ static const struct rt5645_platform_data lattepanda_board_platform_data = {
.inv_jd1_1 = true
};
+static const struct rt5645_platform_data kahlee_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .jd_mode = 3,
+};
+
static const struct dmi_system_id dmi_platform_data[] = {
{
.ident = "Chrome Buddy",
@@ -3745,6 +3751,13 @@ static const struct dmi_system_id dmi_platform_data[] = {
},
.driver_data = (void *)&lattepanda_board_platform_data,
},
+ {
+ .ident = "Chrome Kahlee",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kahlee"),
+ },
+ .driver_data = (void *)&kahlee_platform_data,
+ },
{ }
};
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index 2943692f66ed..e6c1ec6c426e 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -3644,7 +3644,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_1,
RT5663_LDO1_DVO_MASK | RT5663_AMP_HP_MASK,
RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X);
- break;
+ break;
case CODEC_VER_0:
regmap_update_bits(rt5663->regmap, RT5663_DIG_MISC,
RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN);
@@ -3663,7 +3663,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5663->regmap, RT5663_TDM_2,
RT5663_DATA_SWAP_ADCDAT1_MASK,
RT5663_DATA_SWAP_ADCDAT1_LL);
- break;
+ break;
default:
dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__);
}
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
index d681488f5312..25e28be3722e 100644
--- a/sound/soc/codecs/rt5677-spi.c
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -24,6 +24,9 @@
#include <linux/firmware.h>
#include <linux/acpi.h>
+#include <sound/soc.h>
+
+#include "rt5677.h"
#include "rt5677-spi.h"
#define DRV_NAME "rt5677spi"
@@ -45,9 +48,368 @@
#define RT5677_SPI_WRITE_16 0x1
#define RT5677_SPI_READ_16 0x0
+#define RT5677_BUF_BYTES_TOTAL 0x20000
+#define RT5677_MIC_BUF_ADDR 0x60030000
+#define RT5677_MODEL_ADDR 0x5FFC9800
+#define RT5677_MIC_BUF_BYTES ((u32)(RT5677_BUF_BYTES_TOTAL - \
+ sizeof(u32)))
+#define RT5677_MIC_BUF_FIRST_READ_SIZE 0x10000
+
static struct spi_device *g_spi;
static DEFINE_MUTEX(spi_mutex);
+struct rt5677_dsp {
+ struct device *dev;
+ struct delayed_work copy_work;
+ struct mutex dma_lock;
+ struct snd_pcm_substream *substream;
+ size_t dma_offset; /* zero-based offset into runtime->dma_area */
+ size_t avail_bytes; /* number of new bytes since last period */
+ u32 mic_read_offset; /* zero-based offset into DSP's mic buffer */
+ bool new_hotword; /* a new hotword is fired */
+};
+
+static const struct snd_pcm_hardware rt5677_spi_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = RT5677_BUF_BYTES_TOTAL / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = RT5677_BUF_BYTES_TOTAL,
+};
+
+static struct snd_soc_dai_driver rt5677_spi_dai = {
+ /* The DAI name "rt5677-dsp-cpu-dai" is not used. The actual DAI name
+ * registered with ASoC is the name of the device "spi-RT5677AA:00",
+ * because we only have one DAI. See snd_soc_register_dais().
+ */
+ .name = "rt5677-dsp-cpu-dai",
+ .id = 0,
+ .capture = {
+ .stream_name = "DSP Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5677_spi_pcm_open(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &rt5677_spi_pcm_hardware);
+ return 0;
+}
+
+static int rt5677_spi_pcm_close(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *codec_component =
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
+ struct rt5677_priv *rt5677 =
+ snd_soc_component_get_drvdata(codec_component);
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt5677_dsp->copy_work);
+ rt5677->set_dsp_vad(codec_component, false);
+ return 0;
+}
+
+static int rt5677_spi_hw_params(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&rt5677_dsp->dma_lock);
+ ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ rt5677_dsp->substream = substream;
+ mutex_unlock(&rt5677_dsp->dma_lock);
+
+ return ret;
+}
+
+static int rt5677_spi_hw_free(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5677_dsp->dma_lock);
+ rt5677_dsp->substream = NULL;
+ mutex_unlock(&rt5677_dsp->dma_lock);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int rt5677_spi_prepare(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *rt5677_component =
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
+ struct rt5677_priv *rt5677 =
+ snd_soc_component_get_drvdata(rt5677_component);
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ rt5677->set_dsp_vad(rt5677_component, true);
+ rt5677_dsp->dma_offset = 0;
+ rt5677_dsp->avail_bytes = 0;
+ return 0;
+}
+
+static snd_pcm_uframes_t rt5677_spi_pcm_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ return bytes_to_frames(runtime, rt5677_dsp->dma_offset);
+}
+
+static int rt5677_spi_mic_write_offset(u32 *mic_write_offset)
+{
+ int ret;
+ /* Grab the first 4 bytes that hold the write pointer on the
+ * dsp, and check to make sure that it points somewhere inside the
+ * buffer.
+ */
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR, mic_write_offset,
+ sizeof(u32));
+ if (ret)
+ return ret;
+ /* Adjust the offset so that it's zero-based */
+ *mic_write_offset = *mic_write_offset - sizeof(u32);
+ return *mic_write_offset < RT5677_MIC_BUF_BYTES ? 0 : -EFAULT;
+}
+
+/*
+ * Copy one contiguous block of audio samples from the DSP mic buffer to the
+ * dma_area of the pcm runtime. The receiving buffer may wrap around.
+ * @begin: start offset of the block to copy, in bytes.
+ * @end: offset of the first byte after the block to copy, must be greater
+ * than or equal to begin.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+static int rt5677_spi_copy_block(struct rt5677_dsp *rt5677_dsp,
+ u32 begin, u32 end)
+{
+ struct snd_pcm_runtime *runtime = rt5677_dsp->substream->runtime;
+ size_t bytes_per_frame = frames_to_bytes(runtime, 1);
+ size_t first_chunk_len, second_chunk_len;
+ int ret;
+
+ if (begin > end || runtime->dma_bytes < 2 * bytes_per_frame) {
+ dev_err(rt5677_dsp->dev,
+ "Invalid copy from (%u, %u), dma_area size %zu\n",
+ begin, end, runtime->dma_bytes);
+ return -EINVAL;
+ }
+
+ /* The block to copy is empty */
+ if (begin == end)
+ return 0;
+
+ /* If the incoming chunk is too big for the receiving buffer, only the
+ * last "receiving buffer size - one frame" bytes are copied.
+ */
+ if (end - begin > runtime->dma_bytes - bytes_per_frame)
+ begin = end - (runtime->dma_bytes - bytes_per_frame);
+
+ /* May need to split to two chunks, calculate the size of each */
+ first_chunk_len = end - begin;
+ second_chunk_len = 0;
+ if (rt5677_dsp->dma_offset + first_chunk_len > runtime->dma_bytes) {
+ /* Receiving buffer wrapped around */
+ second_chunk_len = first_chunk_len;
+ first_chunk_len = runtime->dma_bytes - rt5677_dsp->dma_offset;
+ second_chunk_len -= first_chunk_len;
+ }
+
+ /* Copy first chunk */
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + begin,
+ runtime->dma_area + rt5677_dsp->dma_offset,
+ first_chunk_len);
+ if (ret)
+ return ret;
+ rt5677_dsp->dma_offset += first_chunk_len;
+ if (rt5677_dsp->dma_offset == runtime->dma_bytes)
+ rt5677_dsp->dma_offset = 0;
+
+ /* Copy second chunk */
+ if (second_chunk_len) {
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) +
+ begin + first_chunk_len, runtime->dma_area,
+ second_chunk_len);
+ if (!ret)
+ rt5677_dsp->dma_offset = second_chunk_len;
+ }
+ return ret;
+}
+
+/*
+ * Copy a given amount of audio samples from the DSP mic buffer starting at
+ * mic_read_offset, to the dma_area of the pcm runtime. The source buffer may
+ * wrap around. mic_read_offset is updated after successful copy.
+ * @amount: amount of samples to copy, in bytes.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount)
+{
+ int ret = 0;
+ u32 target;
+
+ if (amount == 0)
+ return ret;
+
+ target = rt5677_dsp->mic_read_offset + amount;
+ /* Copy the first chunk in DSP's mic buffer */
+ ret |= rt5677_spi_copy_block(rt5677_dsp, rt5677_dsp->mic_read_offset,
+ min(target, RT5677_MIC_BUF_BYTES));
+
+ if (target >= RT5677_MIC_BUF_BYTES) {
+ /* Wrap around, copy the second chunk */
+ target -= RT5677_MIC_BUF_BYTES;
+ ret |= rt5677_spi_copy_block(rt5677_dsp, 0, target);
+ }
+
+ if (!ret)
+ rt5677_dsp->mic_read_offset = target;
+ return ret;
+}
+
+/*
+ * A delayed work that streams audio samples from the DSP mic buffer to the
+ * dma_area of the pcm runtime via SPI.
+ */
+static void rt5677_spi_copy_work(struct work_struct *work)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ container_of(work, struct rt5677_dsp, copy_work.work);
+ struct snd_pcm_runtime *runtime;
+ u32 mic_write_offset;
+ size_t new_bytes, copy_bytes, period_bytes;
+ unsigned int delay;
+ int ret = 0;
+
+ /* Ensure runtime->dma_area buffer does not go away while copying. */
+ mutex_lock(&rt5677_dsp->dma_lock);
+ if (!rt5677_dsp->substream) {
+ dev_err(rt5677_dsp->dev, "No pcm substream\n");
+ goto done;
+ }
+
+ runtime = rt5677_dsp->substream->runtime;
+
+ if (rt5677_spi_mic_write_offset(&mic_write_offset)) {
+ dev_err(rt5677_dsp->dev, "No mic_write_offset\n");
+ goto done;
+ }
+
+ /* If this is the first time that we've asked for streaming data after
+ * a hotword is fired, we should start reading from the previous 2
+ * seconds of audio from wherever the mic_write_offset is currently.
+ */
+ if (rt5677_dsp->new_hotword) {
+ rt5677_dsp->new_hotword = false;
+ /* See if buffer wraparound happens */
+ if (mic_write_offset < RT5677_MIC_BUF_FIRST_READ_SIZE)
+ rt5677_dsp->mic_read_offset = RT5677_MIC_BUF_BYTES -
+ (RT5677_MIC_BUF_FIRST_READ_SIZE -
+ mic_write_offset);
+ else
+ rt5677_dsp->mic_read_offset = mic_write_offset -
+ RT5677_MIC_BUF_FIRST_READ_SIZE;
+ }
+
+ /* Calculate the amount of new samples in bytes */
+ if (rt5677_dsp->mic_read_offset <= mic_write_offset)
+ new_bytes = mic_write_offset - rt5677_dsp->mic_read_offset;
+ else
+ new_bytes = RT5677_MIC_BUF_BYTES + mic_write_offset
+ - rt5677_dsp->mic_read_offset;
+
+ /* Copy all new samples from DSP mic buffer, one period at a time */
+ period_bytes = snd_pcm_lib_period_bytes(rt5677_dsp->substream);
+ while (new_bytes) {
+ copy_bytes = min(new_bytes, period_bytes
+ - rt5677_dsp->avail_bytes);
+ ret = rt5677_spi_copy(rt5677_dsp, copy_bytes);
+ if (ret) {
+ dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret);
+ goto done;
+ }
+ rt5677_dsp->avail_bytes += copy_bytes;
+ if (rt5677_dsp->avail_bytes >= period_bytes) {
+ snd_pcm_period_elapsed(rt5677_dsp->substream);
+ rt5677_dsp->avail_bytes = 0;
+ }
+ new_bytes -= copy_bytes;
+ }
+
+ delay = bytes_to_frames(runtime, period_bytes) / (runtime->rate / 1000);
+ schedule_delayed_work(&rt5677_dsp->copy_work, msecs_to_jiffies(delay));
+done:
+ mutex_unlock(&rt5677_dsp->dma_lock);
+}
+
+static struct page *rt5677_spi_pcm_page(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return snd_pcm_lib_get_vmalloc_page(substream, offset);
+}
+
+static int rt5677_spi_pcm_probe(struct snd_soc_component *component)
+{
+ struct rt5677_dsp *rt5677_dsp;
+
+ rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp),
+ GFP_KERNEL);
+ if (!rt5677_dsp)
+ return -ENOMEM;
+ rt5677_dsp->dev = &g_spi->dev;
+ mutex_init(&rt5677_dsp->dma_lock);
+ INIT_DELAYED_WORK(&rt5677_dsp->copy_work, rt5677_spi_copy_work);
+
+ snd_soc_component_set_drvdata(component, rt5677_dsp);
+ return 0;
+}
+
+static const struct snd_soc_component_driver rt5677_spi_dai_component = {
+ .name = DRV_NAME,
+ .probe = rt5677_spi_pcm_probe,
+ .open = rt5677_spi_pcm_open,
+ .close = rt5677_spi_pcm_close,
+ .hw_params = rt5677_spi_hw_params,
+ .hw_free = rt5677_spi_hw_free,
+ .prepare = rt5677_spi_prepare,
+ .pointer = rt5677_spi_pcm_pointer,
+ .page = rt5677_spi_pcm_page,
+};
+
/* Select a suitable transfer command for the next transfer to ensure
* the transfer address is always naturally aligned while minimizing
* the total number of transfers required.
@@ -218,9 +580,45 @@ int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw)
}
EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware);
+void rt5677_spi_hotword_detected(void)
+{
+ struct rt5677_dsp *rt5677_dsp;
+
+ if (!g_spi)
+ return;
+
+ rt5677_dsp = dev_get_drvdata(&g_spi->dev);
+ if (!rt5677_dsp) {
+ dev_err(&g_spi->dev, "Can't get rt5677_dsp\n");
+ return;
+ }
+
+ mutex_lock(&rt5677_dsp->dma_lock);
+ dev_info(rt5677_dsp->dev, "Hotword detected\n");
+ rt5677_dsp->new_hotword = true;
+ mutex_unlock(&rt5677_dsp->dma_lock);
+
+ schedule_delayed_work(&rt5677_dsp->copy_work, 0);
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_hotword_detected);
+
static int rt5677_spi_probe(struct spi_device *spi)
{
+ int ret;
+
g_spi = spi;
+
+ ret = snd_soc_register_component(&spi->dev, &rt5677_spi_dai_component,
+ &rt5677_spi_dai, 1);
+ if (ret < 0)
+ dev_err(&spi->dev, "Failed to register component.\n");
+
+ return ret;
+}
+
+static int rt5677_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_component(&spi->dev);
return 0;
}
@@ -236,6 +634,7 @@ static struct spi_driver rt5677_spi_driver = {
.acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
},
.probe = rt5677_spi_probe,
+ .remove = rt5677_spi_remove,
};
module_spi_driver(rt5677_spi_driver);
diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h
index 6ba3369dc235..3af36ec928e9 100644
--- a/sound/soc/codecs/rt5677-spi.h
+++ b/sound/soc/codecs/rt5677-spi.h
@@ -12,5 +12,6 @@
int rt5677_spi_read(u32 addr, void *rxbuf, size_t len);
int rt5677_spi_write(u32 addr, const void *txbuf, size_t len);
int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw);
+void rt5677_spi_hotword_detected(void);
#endif /* __RT5677_SPI_H__ */
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 315a3d39bc09..e9a051a50ab2 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -38,6 +38,10 @@
#define RT5677_DEVICE_ID 0x6327
+/* Register controlling boot vector */
+#define RT5677_DSP_BOOT_VECTOR 0x1801f090
+#define RT5677_MODEL_ADDR 0x5FFC9800
+
#define RT5677_PR_RANGE_BASE (0xff + 1)
#define RT5677_PR_SPACING 0x100
@@ -298,6 +302,7 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_I2C_MASTER_CTRL7:
case RT5677_I2C_MASTER_CTRL8:
case RT5677_HAP_GENE_CTRL2:
+ case RT5677_PWR_ANLG2: /* Modified by DSP firmware */
case RT5677_PWR_DSP_ST:
case RT5677_PRIV_DATA:
case RT5677_ASRC_22:
@@ -308,6 +313,8 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_IRQ_CTRL1:
case RT5677_IRQ_CTRL2:
case RT5677_GPIO_ST:
+ case RT5677_GPIO_CTRL1: /* Modified by DSP firmware */
+ case RT5677_GPIO_CTRL2: /* Modified by DSP firmware */
case RT5677_DSP_INB1_SRC_CTRL4:
case RT5677_DSP_INB2_SRC_CTRL4:
case RT5677_DSP_INB3_SRC_CTRL4:
@@ -686,10 +693,8 @@ static int rt5677_dsp_mode_i2c_read(
return ret;
}
-static void rt5677_set_dsp_mode(struct snd_soc_component *component, bool on)
+static void rt5677_set_dsp_mode(struct rt5677_priv *rt5677, bool on)
{
- struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
-
if (on) {
regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
RT5677_PWR_DSP, RT5677_PWR_DSP);
@@ -701,86 +706,259 @@ static void rt5677_set_dsp_mode(struct snd_soc_component *component, bool on)
}
}
+static unsigned int rt5677_set_vad_source(struct rt5677_priv *rt5677)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(rt5677->component);
+ /* Force dapm to sync before we enable the
+ * DSP to prevent write corruption
+ */
+ snd_soc_dapm_sync(dapm);
+
+ /* DMIC1 power = enabled
+ * DMIC CLK = 256 * fs / 12
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1,
+ RT5677_DMIC_CLK_MASK, 5 << RT5677_DMIC_CLK_SFT);
+
+ /* I2S pre divide 2 = /6 (clk_sys2) */
+ regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+ RT5677_I2S_PD2_MASK, RT5677_I2S_PD2_6);
+
+ /* DSP Clock = MCLK1 (bypassed PLL2) */
+ regmap_write(rt5677->regmap, RT5677_GLB_CLK2,
+ RT5677_DSP_CLK_SRC_BYPASS);
+
+ /* SAD Threshold1 */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL2, 0x013f);
+ /* SAD Threshold2 */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL3, 0x0ae5);
+ /* SAD Sample Rate Converter = Up 6 (8K to 48K)
+ * SAD Output Sample Rate = Same as I2S
+ * SAD Threshold3
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_VAD_CTRL4,
+ RT5677_VAD_OUT_SRC_RATE_MASK | RT5677_VAD_OUT_SRC_MASK |
+ RT5677_VAD_LV_DIFF_MASK, 0x7f << RT5677_VAD_LV_DIFF_SFT);
+ /* Minimum frame level within a pre-determined duration = 32 frames
+ * Bypass ADPCM Encoder/Decoder = Bypass ADPCM
+ * Automatic Push Data to SAD Buffer Once SAD Flag is triggered = enable
+ * SAD Buffer Over-Writing = enable
+ * SAD Buffer Pop Mode Control = disable
+ * SAD Buffer Push Mode Control = enable
+ * SAD Detector Control = enable
+ * SAD Function Control = enable
+ * SAD Function Reset = normal
+ */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1,
+ RT5677_VAD_FUNC_RESET | RT5677_VAD_FUNC_ENABLE |
+ RT5677_VAD_DET_ENABLE | RT5677_VAD_BUF_PUSH |
+ RT5677_VAD_BUF_OW | RT5677_VAD_FG2ENC |
+ RT5677_VAD_ADPCM_BYPASS | 1 << RT5677_VAD_MIN_DUR_SFT);
+
+ /* VAD/SAD is not routed to the IRQ output (i.e. MX-BE[14] = 0), but it
+ * is routed to DSP_IRQ_0, so DSP firmware may use it to sleep and save
+ * power. See ALC5677 datasheet section 9.17 "GPIO, Interrupt and Jack
+ * Detection" for more info.
+ */
+
+ /* Private register, no doc */
+ regmap_update_bits(rt5677->regmap, RT5677_PR_BASE + RT5677_BIAS_CUR4,
+ 0x0f00, 0x0100);
+
+ /* LDO2 output = 1.2V
+ * LDO1 output = 1.2V (LDO_IN = 1.8V)
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+ RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
+ 5 << RT5677_LDO1_SEL_SFT | 5 << RT5677_LDO2_SEL_SFT);
+
+ /* Codec core power = power on
+ * LDO1 power = power on
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_CORE | RT5677_PWR_LDO1,
+ RT5677_PWR_CORE | RT5677_PWR_LDO1);
+
+ /* Isolation for DCVDD4 = normal (set during probe)
+ * Isolation for DCVDD2 = normal (set during probe)
+ * Isolation for DSP = normal
+ * Isolation for Band 0~7 = disable
+ * Isolation for InBound 4~10 and OutBound 4~10 = disable
+ */
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP2,
+ RT5677_PWR_CORE_ISO | RT5677_PWR_DSP_ISO |
+ RT5677_PWR_SR7_ISO | RT5677_PWR_SR6_ISO |
+ RT5677_PWR_SR5_ISO | RT5677_PWR_SR4_ISO |
+ RT5677_PWR_SR3_ISO | RT5677_PWR_SR2_ISO |
+ RT5677_PWR_SR1_ISO | RT5677_PWR_SR0_ISO |
+ RT5677_PWR_MLT_ISO);
+
+ /* System Band 0~7 = power on
+ * InBound 4~10 and OutBound 4~10 = power on
+ * DSP = power on
+ * DSP CPU = stop (will be set to "run" after firmware loaded)
+ */
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_SR7 | RT5677_PWR_SR6 |
+ RT5677_PWR_SR5 | RT5677_PWR_SR4 |
+ RT5677_PWR_SR3 | RT5677_PWR_SR2 |
+ RT5677_PWR_SR1 | RT5677_PWR_SR0 |
+ RT5677_PWR_MLT | RT5677_PWR_DSP |
+ RT5677_PWR_DSP_CPU);
+
+ return 0;
+}
+
+static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf,
+ unsigned int len)
+{
+ struct snd_soc_component *component = rt5677->component;
+ Elf32_Ehdr *elf_hdr;
+ Elf32_Phdr *pr_hdr;
+ Elf32_Half i;
+ int ret = 0;
+
+ if (!buf || (len < sizeof(Elf32_Ehdr)))
+ return -ENOMEM;
+
+ elf_hdr = (Elf32_Ehdr *)buf;
+#ifndef EM_XTENSA
+#define EM_XTENSA 94
+#endif
+ if (strncmp(elf_hdr->e_ident, ELFMAG, sizeof(ELFMAG) - 1))
+ dev_err(component->dev, "Wrong ELF header prefix\n");
+ if (elf_hdr->e_ehsize != sizeof(Elf32_Ehdr))
+ dev_err(component->dev, "Wrong Elf header size\n");
+ if (elf_hdr->e_machine != EM_XTENSA)
+ dev_err(component->dev, "Wrong DSP code file\n");
+
+ if (len < elf_hdr->e_phoff)
+ return -ENOMEM;
+ pr_hdr = (Elf32_Phdr *)(buf + elf_hdr->e_phoff);
+ for (i = 0; i < elf_hdr->e_phnum; i++) {
+ /* TODO: handle p_memsz != p_filesz */
+ if (pr_hdr->p_paddr && pr_hdr->p_filesz) {
+ dev_info(component->dev, "Load 0x%x bytes to 0x%x\n",
+ pr_hdr->p_filesz, pr_hdr->p_paddr);
+
+ ret = rt5677_spi_write(pr_hdr->p_paddr,
+ buf + pr_hdr->p_offset,
+ pr_hdr->p_filesz);
+ if (ret)
+ dev_err(component->dev, "Load firmware failed %d\n",
+ ret);
+ }
+ pr_hdr++;
+ }
+ return ret;
+}
+
+static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677)
+{
+ const struct firmware *fwp;
+ struct device *dev = rt5677->component->dev;
+ int ret = 0;
+
+ /* Load dsp firmware from rt5677_elf_vad file */
+ ret = request_firmware(&fwp, "rt5677_elf_vad", dev);
+ if (ret) {
+ dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret);
+ return ret;
+ }
+ dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size);
+
+ ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size);
+ release_firmware(fwp);
+ return ret;
+}
+
static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- static bool activity;
- int ret;
+ rt5677->dsp_vad_en_request = on;
+ rt5677->dsp_vad_en = on;
if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI))
return -ENXIO;
- if (on && !activity) {
+ schedule_delayed_work(&rt5677->dsp_work, 0);
+ return 0;
+}
+
+static void rt5677_dsp_work(struct work_struct *work)
+{
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, dsp_work.work);
+ static bool activity;
+ bool enable = rt5677->dsp_vad_en;
+ int i, val;
+
+
+ dev_info(rt5677->component->dev, "DSP VAD: enable=%d, activity=%d\n",
+ enable, activity);
+
+ if (enable && !activity) {
activity = true;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, true);
+ /* Before a hotword is detected, GPIO1 pin is configured as IRQ
+ * output so that jack detect works. When a hotword is detected,
+ * the DSP firmware configures the GPIO1 pin as GPIO1 and
+ * drives a 1. rt5677_irq() is called after a rising edge on
+ * the GPIO1 pin, due to either jack detect event or hotword
+ * event, or both. All possible events are checked and handled
+ * in rt5677_irq() where GPIO1 pin is configured back to IRQ
+ * output if a hotword is detected.
+ */
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1);
- regmap_update_bits(rt5677->regmap,
- RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
- RT5677_LDO1_SEL_MASK, 0x0);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
- RT5677_PWR_LDO1, RT5677_PWR_LDO1);
- switch (rt5677->type) {
- case RT5677:
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
- RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
- RT5677_PLL2_PR_SRC_MASK |
- RT5677_DSP_CLK_SRC_MASK,
- RT5677_PLL2_PR_SRC_MCLK2 |
- RT5677_DSP_CLK_SRC_BYPASS);
- break;
- case RT5676:
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
- RT5677_DSP_CLK_SRC_MASK,
- RT5677_DSP_CLK_SRC_BYPASS);
- break;
- default:
- break;
+ rt5677_set_vad_source(rt5677);
+ rt5677_set_dsp_mode(rt5677, true);
+
+#define RT5677_BOOT_RETRY 20
+ for (i = 0; i < RT5677_BOOT_RETRY; i++) {
+ regmap_read(rt5677->regmap, RT5677_PWR_DSP_ST, &val);
+ if (val == 0x3ff)
+ break;
+ udelay(500);
}
- regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd);
- rt5677_set_dsp_mode(component, true);
-
- ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1,
- component->dev);
- if (ret == 0) {
- rt5677_spi_write_firmware(0x50000000, rt5677->fw1);
- release_firmware(rt5677->fw1);
+ if (i == RT5677_BOOT_RETRY && val != 0x3ff) {
+ dev_err(rt5677->component->dev, "DSP Boot Timed Out!");
+ return;
}
- ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
- component->dev);
- if (ret == 0) {
- rt5677_spi_write_firmware(0x60000000, rt5677->fw2);
- release_firmware(rt5677->fw2);
- }
+ /* Boot the firmware from IRAM instead of SRAM0. */
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0009, 0x0003);
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0019, 0x0003);
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0009, 0x0003);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
+ rt5677_load_dsp_from_file(rt5677);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_cache_only(rt5677->regmap, true);
- } else if (!on && activity) {
+ /* Set DSP CPU to Run */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP_CPU, 0x0);
+ } else if (!enable && activity) {
activity = false;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, true);
+ /* Don't turn off the DSP while handling irqs */
+ mutex_lock(&rt5677->irq_lock);
+ /* Set DSP CPU to Stop */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
- rt5677_set_dsp_mode(component, false);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
+ rt5677_set_dsp_mode(rt5677, false);
- regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+ /* Disable and clear VAD interrupt */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_mark_dirty(rt5677->regmap);
- regcache_sync(rt5677->regmap);
- }
+ /* Set GPIO1 pin back to be IRQ output for jack detect */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
- return 0;
+ mutex_unlock(&rt5677->irq_lock);
+ }
}
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
@@ -805,7 +983,7 @@ static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- ucontrol->value.integer.value[0] = rt5677->dsp_vad_en;
+ ucontrol->value.integer.value[0] = rt5677->dsp_vad_en_request;
return 0;
}
@@ -814,12 +992,8 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
-
- rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
- if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
- rt5677_set_dsp_vad(component, rt5677->dsp_vad_en);
+ rt5677_set_dsp_vad(component, !!ucontrol->value.integer.value[0]);
return 0;
}
@@ -3010,6 +3184,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SLBRX", "SLIMBus Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("SLBTX", "SLIMBus Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DSPTX", "DSP Buffer", 0, SND_SOC_NOPM, 0, 0),
/* Sidetone Mux */
SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
@@ -3544,11 +3719,24 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "SLBTX", NULL, "SLB ADC3 Mux" },
{ "SLBTX", NULL, "SLB ADC4 Mux" },
+ { "DSPTX", NULL, "IB01 Bypass Mux" },
+
{ "IB01 Mux", "IF1 DAC 01", "IF1 DAC01" },
{ "IB01 Mux", "IF2 DAC 01", "IF2 DAC01" },
{ "IB01 Mux", "SLB DAC 01", "SLB DAC01" },
{ "IB01 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
- { "IB01 Mux", "VAD ADC/DAC1 FS", "DAC1 FS" },
+ /* The IB01 Mux controls the source for InBound0 and InBound1.
+ * When the mux option "VAD ADC/DAC1 FS" is selected, "VAD ADC" goes to
+ * InBound0 and "DAC1 FS" goes to InBound1. "VAD ADC" is used for
+ * hotwording. "DAC1 FS" is not used currently.
+ *
+ * Creating a common widget node for "VAD ADC" + "DAC1 FS" and
+ * connecting the common widget to IB01 Mux causes the issue where
+ * there is an active path going from system playback -> "DAC1 FS" ->
+ * IB01 Mux -> DSP Buffer -> hotword stream. This wrong path confuses
+ * DAPM. Therefore "DAC1 FS" is ignored for now.
+ */
+ { "IB01 Mux", "VAD ADC/DAC1 FS", "VAD ADC Mux" },
{ "IB01 Bypass Mux", "Bypass", "IB01 Mux" },
{ "IB01 Bypass Mux", "Pass SRC", "IB01 Mux" },
@@ -4457,14 +4645,15 @@ static int rt5677_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ enum snd_soc_bias_level prev_bias =
+ snd_soc_component_get_bias_level(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) {
- rt5677_set_dsp_vad(component, false);
+ if (prev_bias == SND_SOC_BIAS_STANDBY) {
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
@@ -4488,9 +4677,25 @@ static int rt5677_set_bias_level(struct snd_soc_component *component,
break;
case SND_SOC_BIAS_STANDBY:
+ if (prev_bias == SND_SOC_BIAS_OFF &&
+ rt5677->dsp_vad_en_request) {
+ /* Re-enable the DSP if it was turned off at suspend */
+ rt5677->dsp_vad_en = true;
+ /* The delay is to wait for MCLK */
+ schedule_delayed_work(&rt5677->dsp_work,
+ msecs_to_jiffies(1000));
+ }
break;
case SND_SOC_BIAS_OFF:
+ flush_delayed_work(&rt5677->dsp_work);
+ if (rt5677->is_dsp_mode) {
+ /* Turn off the DSP before suspend */
+ rt5677->dsp_vad_en = false;
+ schedule_delayed_work(&rt5677->dsp_work, 0);
+ flush_delayed_work(&rt5677->dsp_work);
+ }
+
regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x0);
regmap_write(rt5677->regmap, RT5677_PWR_DIG1, 0x0000);
regmap_write(rt5677->regmap, RT5677_PWR_ANLG1,
@@ -4740,6 +4945,8 @@ static void rt5677_remove(struct snd_soc_component *component)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ cancel_delayed_work_sync(&rt5677->dsp_work);
+
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
@@ -4750,6 +4957,11 @@ static int rt5677_suspend(struct snd_soc_component *component)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ if (rt5677->irq) {
+ cancel_delayed_work_sync(&rt5677->resume_irq_check);
+ disable_irq(rt5677->irq);
+ }
+
if (!rt5677->dsp_vad_en) {
regcache_cache_only(rt5677->regmap, true);
regcache_mark_dirty(rt5677->regmap);
@@ -4778,6 +4990,11 @@ static int rt5677_resume(struct snd_soc_component *component)
regcache_sync(rt5677->regmap);
}
+ if (rt5677->irq) {
+ enable_irq(rt5677->irq);
+ schedule_delayed_work(&rt5677->resume_irq_check, 0);
+ }
+
return 0;
}
#else
@@ -4842,6 +5059,11 @@ static const struct snd_soc_dai_ops rt5677_aif_dai_ops = {
.set_tdm_slot = rt5677_set_tdm_slot,
};
+static const struct snd_soc_dai_ops rt5677_dsp_dai_ops = {
+ .set_sysclk = rt5677_set_dai_sysclk,
+ .set_pll = rt5677_set_dai_pll,
+};
+
static struct snd_soc_dai_driver rt5677_dai[] = {
{
.name = "rt5677-aif1",
@@ -4938,6 +5160,18 @@ static struct snd_soc_dai_driver rt5677_dai[] = {
},
.ops = &rt5677_aif_dai_ops,
},
+ {
+ .name = "rt5677-dspbuffer",
+ .id = RT5677_DSPBUFF,
+ .capture = {
+ .stream_name = "DSP Buffer",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &rt5677_dsp_dai_ops,
+ },
};
static const struct snd_soc_component_driver soc_component_dev_rt5677 = {
@@ -5073,6 +5307,28 @@ static const struct rt5677_irq_desc rt5677_irq_descs[] = {
},
};
+static bool rt5677_check_hotword(struct rt5677_priv *rt5677)
+{
+ int reg_gpio;
+
+ if (!rt5677->is_dsp_mode)
+ return false;
+
+ if (regmap_read(rt5677->regmap, RT5677_GPIO_CTRL1, &reg_gpio))
+ return false;
+
+ /* Firmware sets GPIO1 pin to be GPIO1 after hotword is detected */
+ if ((reg_gpio & RT5677_GPIO1_PIN_MASK) == RT5677_GPIO1_PIN_IRQ)
+ return false;
+
+ /* Set GPIO1 pin back to be IRQ output for jack detect */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
+
+ rt5677_spi_hotword_detected();
+ return true;
+}
+
static irqreturn_t rt5677_irq(int unused, void *data)
{
struct rt5677_priv *rt5677 = data;
@@ -5118,7 +5374,13 @@ static irqreturn_t rt5677_irq(int unused, void *data)
reg_irq ^= rt5677_irq_descs[i].polarity_mask;
}
}
- if (!irq_fired)
+
+ /* Exit the loop only when we know for sure that GPIO1 pin
+ * was low at some point since irq_lock was acquired. Any event
+ * after that point creates a rising edge that triggers another
+ * call to rt5677_irq().
+ */
+ if (!irq_fired && !rt5677_check_hotword(rt5677))
goto exit;
ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
@@ -5129,6 +5391,7 @@ static irqreturn_t rt5677_irq(int unused, void *data)
}
}
exit:
+ WARN_ON_ONCE(loop == 20);
mutex_unlock(&rt5677->irq_lock);
if (irq_fired)
return IRQ_HANDLED;
@@ -5136,6 +5399,39 @@ exit:
return IRQ_NONE;
}
+static void rt5677_resume_irq_check(struct work_struct *work)
+{
+ int i, virq;
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, resume_irq_check.work);
+
+ /* This is needed to check and clear the interrupt status register
+ * at resume. If the headset is plugged/unplugged when the device is
+ * fully suspended, there won't be a rising edge at resume to trigger
+ * the interrupt. Without this, we miss the next unplug/plug event.
+ */
+ rt5677_irq(0, rt5677);
+
+ /* Call all enabled jack detect irq handlers again. This is needed in
+ * addition to the above check for a corner case caused by jack gpio
+ * debounce. After codec irq is disabled at suspend, the delayed work
+ * scheduled by soc-jack may run and read wrong jack gpio values, since
+ * the regmap is in cache only mode. At resume, there is no irq because
+ * rt5677_irq has already ran and cleared the irq status at suspend.
+ * Without this explicit check, unplug the headset right after suspend
+ * starts, then after resume the headset is still shown as plugged in.
+ */
+ mutex_lock(&rt5677->irq_lock);
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
+ }
+ mutex_unlock(&rt5677->irq_lock);
+}
+
static void rt5677_irq_bus_lock(struct irq_data *data)
{
struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
@@ -5211,6 +5507,7 @@ static int rt5677_init_irq(struct i2c_client *i2c)
}
mutex_init(&rt5677->irq_lock);
+ INIT_DELAYED_WORK(&rt5677->resume_irq_check, rt5677_resume_irq_check);
/*
* Select RC as the debounce clock so that GPIO works even when
@@ -5256,6 +5553,8 @@ static int rt5677_init_irq(struct i2c_client *i2c)
if (ret)
dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ rt5677->irq = i2c->irq;
+
return ret;
}
@@ -5271,6 +5570,8 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
return -ENOMEM;
rt5677->dev = &i2c->dev;
+ rt5677->set_dsp_vad = rt5677_set_dsp_vad;
+ INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work);
i2c_set_clientdata(i2c, rt5677);
if (i2c->dev.of_node) {
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index 213f4b8ca269..944ae02aafc2 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1336,6 +1336,8 @@
#define RT5677_PLL_M_SFT 12
#define RT5677_PLL_M_BP (0x1 << 11)
#define RT5677_PLL_M_BP_SFT 11
+#define RT5677_PLL_UPDATE_PLL1 (0x1 << 1)
+#define RT5677_PLL_UPDATE_PLL1_SFT 1
/* Global Clock Control 1 (0x80) */
#define RT5677_SCLK_SRC_MASK (0x3 << 14)
@@ -1730,6 +1732,7 @@ enum {
RT5677_AIF4,
RT5677_AIF5,
RT5677_AIFS,
+ RT5677_DSPBUFF,
};
enum {
@@ -1845,14 +1848,20 @@ struct rt5677_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
- bool dsp_vad_en;
+ bool dsp_vad_en_request; /* DSP VAD enable/disable request */
+ bool dsp_vad_en; /* dsp_work parameter */
bool is_dsp_mode;
bool is_vref_slow;
+ struct delayed_work dsp_work;
/* Interrupt handling */
struct irq_domain *domain;
struct mutex irq_lock;
unsigned int irq_en;
+ struct delayed_work resume_irq_check;
+ int irq;
+
+ int (*set_dsp_vad)(struct snd_soc_component *component, bool on);
};
int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 5370e4b00104..b1713fffa3eb 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -44,6 +44,7 @@ static const struct rt5682_platform_data i2s_default_platform_data = {
.dmic1_data_pin = RT5682_DMIC1_DATA_GPIO2,
.dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3,
.jd_src = RT5682_JD1,
+ .btndet_delay = 16,
};
struct rt5682_priv {
@@ -1027,6 +1028,18 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
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;
@@ -2445,6 +2458,8 @@ static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev)
&rt5682->pdata.dmic1_clk_pin);
device_property_read_u32(dev, "realtek,jd-src",
&rt5682->pdata.jd_src);
+ device_property_read_u32(dev, "realtek,btndet-delay",
+ &rt5682->pdata.btndet_delay);
rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
"realtek,ldo1-en-gpios", 0);
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
new file mode 100644
index 000000000000..729acd874c48
--- /dev/null
+++ b/sound/soc/codecs/tas2562.c
@@ -0,0 +1,590 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for the Texas Instruments TAS2562 CODEC
+// Copyright (C) 2019 Texas Instruments Inc.
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tas2562.h"
+
+#define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FORMAT_S32_LE)
+
+struct tas2562_data {
+ struct snd_soc_component *component;
+ struct gpio_desc *sdz_gpio;
+ struct regmap *regmap;
+ struct device *dev;
+ struct i2c_client *client;
+ int v_sense_slot;
+ int i_sense_slot;
+};
+
+static int tas2562_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct tas2562_data *tas2562 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_component_update_bits(component,
+ TAS2562_PWR_CTRL,
+ TAS2562_MODE_MASK, TAS2562_ACTIVE);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component,
+ TAS2562_PWR_CTRL,
+ TAS2562_MODE_MASK, TAS2562_MUTE);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component,
+ TAS2562_PWR_CTRL,
+ TAS2562_MODE_MASK, TAS2562_SHUTDOWN);
+ break;
+
+ default:
+ dev_err(tas2562->dev,
+ "wrong power level setting %d\n", level);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tas2562_set_samplerate(struct tas2562_data *tas2562, int samplerate)
+{
+ int samp_rate;
+ int ramp_rate;
+
+ switch (samplerate) {
+ case 7350:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ;
+ break;
+ case 8000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ;
+ break;
+ case 14700:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ;
+ break;
+ case 16000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ;
+ break;
+ case 22050:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ;
+ break;
+ case 24000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ;
+ break;
+ case 29400:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ;
+ break;
+ case 32000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ;
+ break;
+ case 44100:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ;
+ break;
+ case 48000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ;
+ break;
+ case 88200:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ;
+ break;
+ case 96000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ;
+ break;
+ case 176400:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ;
+ break;
+ case 192000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ;
+ break;
+ default:
+ dev_info(tas2562->dev, "%s, unsupported sample rate, %d\n",
+ __func__, samplerate);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0,
+ TAS2562_TDM_CFG0_RAMPRATE_MASK, ramp_rate);
+ snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0,
+ TAS2562_TDM_CFG0_SAMPRATE_MASK, samp_rate);
+
+ return 0;
+}
+
+static int tas2562_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (slot_width) {
+ case 16:
+ ret = snd_soc_component_update_bits(component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXLEN_MASK,
+ TAS2562_TDM_CFG2_RXLEN_16B);
+ break;
+ case 24:
+ ret = snd_soc_component_update_bits(component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXLEN_MASK,
+ TAS2562_TDM_CFG2_RXLEN_24B);
+ break;
+ case 32:
+ ret = snd_soc_component_update_bits(component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXLEN_MASK,
+ TAS2562_TDM_CFG2_RXLEN_32B);
+ break;
+
+ case 0:
+ /* Do not change slot width */
+ break;
+ default:
+ dev_err(tas2562->dev, "slot width not supported");
+ ret = -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2562_set_bitwidth(struct tas2562_data *tas2562, int bitwidth)
+{
+ int ret;
+
+ switch (bitwidth) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_component_update_bits(tas2562->component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXWLEN_MASK,
+ TAS2562_TDM_CFG2_RXWLEN_16B);
+ tas2562->v_sense_slot = tas2562->i_sense_slot + 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_component_update_bits(tas2562->component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXWLEN_MASK,
+ TAS2562_TDM_CFG2_RXWLEN_24B);
+ tas2562->v_sense_slot = tas2562->i_sense_slot + 4;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ snd_soc_component_update_bits(tas2562->component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXWLEN_MASK,
+ TAS2562_TDM_CFG2_RXWLEN_32B);
+ tas2562->v_sense_slot = tas2562->i_sense_slot + 4;
+ break;
+
+ default:
+ dev_info(tas2562->dev, "Not supported params format\n");
+ }
+
+ ret = snd_soc_component_update_bits(tas2562->component,
+ TAS2562_TDM_CFG5,
+ TAS2562_TDM_CFG5_VSNS_EN | TAS2562_TDM_CFG5_VSNS_SLOT_MASK,
+ TAS2562_TDM_CFG5_VSNS_EN | tas2562->v_sense_slot);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(tas2562->component,
+ TAS2562_TDM_CFG6,
+ TAS2562_TDM_CFG6_ISNS_EN | TAS2562_TDM_CFG6_ISNS_SLOT_MASK,
+ TAS2562_TDM_CFG6_ISNS_EN | tas2562->i_sense_slot);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2562_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 tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = tas2562_set_bitwidth(tas2562, params_format(params));
+ if (ret) {
+ dev_err(tas2562->dev, "set bitwidth failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = tas2562_set_samplerate(tas2562, params_rate(params));
+ if (ret)
+ dev_err(tas2562->dev, "set bitwidth failed, %d\n", ret);
+
+ return ret;
+}
+
+static int tas2562_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ asi_cfg_1 = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ asi_cfg_1 |= TAS2562_TDM_CFG1_RX_FALLING;
+ break;
+ default:
+ dev_err(tas2562->dev, "ASI format Inverse is not found\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1,
+ TAS2562_TDM_CFG1_RX_EDGE_MASK,
+ asi_cfg_1);
+ if (ret < 0) {
+ dev_err(tas2562->dev, "Failed to set RX edge\n");
+ return ret;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case (SND_SOC_DAIFMT_I2S):
+ case (SND_SOC_DAIFMT_DSP_A):
+ case (SND_SOC_DAIFMT_DSP_B):
+ tdm_rx_start_slot = BIT(1);
+ break;
+ case (SND_SOC_DAIFMT_LEFT_J):
+ tdm_rx_start_slot = 0;
+ break;
+ default:
+ dev_err(tas2562->dev, "DAI Format is not found, fmt=0x%x\n",
+ fmt);
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1,
+ TAS2562_TDM_CFG1_RX_OFFSET_MASK,
+ tdm_rx_start_slot);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2562_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_component *component = dai->component;
+
+ return snd_soc_component_update_bits(component, TAS2562_PWR_CTRL,
+ TAS2562_MODE_MASK,
+ mute ? TAS2562_MUTE : 0);
+}
+
+static int tas2562_codec_probe(struct snd_soc_component *component)
+{
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ tas2562->component = component;
+
+ if (tas2562->sdz_gpio)
+ gpiod_set_value_cansleep(tas2562->sdz_gpio, 1);
+
+ ret = snd_soc_component_update_bits(component, TAS2562_PWR_CTRL,
+ TAS2562_MODE_MASK, TAS2562_MUTE);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tas2562_suspend(struct snd_soc_component *component)
+{
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(tas2562->regmap, true);
+ regcache_mark_dirty(tas2562->regmap);
+
+ if (tas2562->sdz_gpio)
+ gpiod_set_value_cansleep(tas2562->sdz_gpio, 0);
+
+ return 0;
+}
+
+static int tas2562_resume(struct snd_soc_component *component)
+{
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+
+ if (tas2562->sdz_gpio)
+ gpiod_set_value_cansleep(tas2562->sdz_gpio, 1);
+
+ regcache_cache_only(tas2562->regmap, false);
+
+ return regcache_sync(tas2562->regmap);
+}
+#else
+#define tas2562_suspend NULL
+#define tas2562_resume NULL
+#endif
+
+static const char * const tas2562_ASI1_src[] = {
+ "I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(tas2562_ASI1_src_enum, TAS2562_TDM_CFG2, 4,
+ tas2562_ASI1_src);
+
+static const struct snd_kcontrol_new tas2562_asi1_mux =
+ SOC_DAPM_ENUM("ASI1 Source", tas2562_ASI1_src_enum);
+
+static int tas2562_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_info(tas2562->dev, "SND_SOC_DAPM_POST_PMU\n");
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_info(tas2562->dev, "SND_SOC_DAPM_PRE_PMD\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(tas2562_dac_tlv, 850, 50, 0);
+
+static const struct snd_kcontrol_new isense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_ISENSE_POWER_EN,
+ 1, 1);
+
+static const struct snd_kcontrol_new vsense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_VSENSE_POWER_EN,
+ 1, 1);
+
+static const struct snd_kcontrol_new tas2562_snd_controls[] = {
+ SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 0, 0x1c, 0,
+ tas2562_dac_tlv),
+};
+
+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),
+ SND_SOC_DAPM_SWITCH("VSENSE", TAS2562_PWR_CTRL, 2, 1, &vsense_switch),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON"),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route tas2562_audio_map[] = {
+ {"ASI1 Sel", "I2C offset", "ASI1"},
+ {"ASI1 Sel", "Left", "ASI1"},
+ {"ASI1 Sel", "Right", "ASI1"},
+ {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+ { "DAC", NULL, "DAC IN" },
+ { "OUT", NULL, "DAC" },
+ {"ISENSE", "Switch", "IMON"},
+ {"VSENSE", "Switch", "VMON"},
+};
+
+static const struct snd_soc_component_driver soc_component_dev_tas2562 = {
+ .probe = tas2562_codec_probe,
+ .suspend = tas2562_suspend,
+ .resume = tas2562_resume,
+ .set_bias_level = tas2562_set_bias_level,
+ .controls = tas2562_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2562_snd_controls),
+ .dapm_widgets = tas2562_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets),
+ .dapm_routes = tas2562_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2562_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static const struct snd_soc_dai_ops tas2562_speaker_dai_ops = {
+ .hw_params = tas2562_hw_params,
+ .set_fmt = tas2562_set_dai_fmt,
+ .set_tdm_slot = tas2562_set_dai_tdm_slot,
+ .digital_mute = tas2562_mute,
+};
+
+static struct snd_soc_dai_driver tas2562_dai[] = {
+ {
+ .name = "tas2562-amplifier",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASI1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2562_FORMATS,
+ },
+ .ops = &tas2562_speaker_dai_ops,
+ },
+};
+
+static const struct regmap_range_cfg tas2562_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 5 * 128,
+ .selector_reg = TAS2562_PAGE_CTRL,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct reg_default tas2562_reg_defaults[] = {
+ { TAS2562_PAGE_CTRL, 0x00 },
+ { TAS2562_SW_RESET, 0x00 },
+ { TAS2562_PWR_CTRL, 0x0e },
+ { TAS2562_PB_CFG1, 0x20 },
+ { TAS2562_TDM_CFG0, 0x09 },
+ { TAS2562_TDM_CFG1, 0x02 },
+};
+
+static const struct regmap_config tas2562_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 5 * 128,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = tas2562_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2562_reg_defaults),
+ .ranges = tas2562_ranges,
+ .num_ranges = ARRAY_SIZE(tas2562_ranges),
+};
+
+static int tas2562_parse_dt(struct tas2562_data *tas2562)
+{
+ struct device *dev = tas2562->dev;
+ int ret = 0;
+
+ tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down-gpio",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2562->sdz_gpio)) {
+ if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER) {
+ tas2562->sdz_gpio = NULL;
+ return -EPROBE_DEFER;
+ }
+ }
+
+ ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
+ &tas2562->i_sense_slot);
+ if (ret)
+ dev_err(dev, "Looking up %s property failed %d\n",
+ "ti,imon-slot-no", ret);
+
+ return ret;
+}
+
+static int tas2562_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct tas2562_data *data;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->dev = &client->dev;
+
+ tas2562_parse_dt(data);
+
+ data->regmap = devm_regmap_init_i2c(client, &tas2562_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(dev, "failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ dev_set_drvdata(&client->dev, data);
+
+ return devm_snd_soc_register_component(dev, &soc_component_dev_tas2562,
+ tas2562_dai,
+ ARRAY_SIZE(tas2562_dai));
+
+}
+
+static const struct i2c_device_id tas2562_id[] = {
+ { "tas2562", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2562_id);
+
+static const struct of_device_id tas2562_of_match[] = {
+ { .compatible = "ti,tas2562", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tas2562_of_match);
+
+static struct i2c_driver tas2562_i2c_driver = {
+ .driver = {
+ .name = "tas2562",
+ .of_match_table = of_match_ptr(tas2562_of_match),
+ },
+ .probe = tas2562_probe,
+ .id_table = tas2562_id,
+};
+
+module_i2c_driver(tas2562_i2c_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("TAS2562 Audio amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2562.h b/sound/soc/codecs/tas2562.h
new file mode 100644
index 000000000000..62e659ab786d
--- /dev/null
+++ b/sound/soc/codecs/tas2562.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tas2562.h - ALSA SoC Texas Instruments TAS2562 Mono Audio Amplifier
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ */
+
+#ifndef __TAS2562_H__
+#define __TAS2562_H__
+
+#define TAS2562_PAGE_CTRL 0x00
+
+#define TAS2562_REG(page, reg) ((page * 128) + reg)
+
+#define TAS2562_SW_RESET TAS2562_REG(0, 0x01)
+#define TAS2562_PWR_CTRL TAS2562_REG(0, 0x02)
+#define TAS2562_PB_CFG1 TAS2562_REG(0, 0x03)
+#define TAS2562_MISC_CFG1 TAS2562_REG(0, 0x04)
+#define TAS2562_MISC_CFG2 TAS2562_REG(0, 0x05)
+
+#define TAS2562_TDM_CFG0 TAS2562_REG(0, 0x06)
+#define TAS2562_TDM_CFG1 TAS2562_REG(0, 0x07)
+#define TAS2562_TDM_CFG2 TAS2562_REG(0, 0x08)
+#define TAS2562_TDM_CFG3 TAS2562_REG(0, 0x09)
+#define TAS2562_TDM_CFG4 TAS2562_REG(0, 0x0a)
+#define TAS2562_TDM_CFG5 TAS2562_REG(0, 0x0b)
+#define TAS2562_TDM_CFG6 TAS2562_REG(0, 0x0c)
+#define TAS2562_TDM_CFG7 TAS2562_REG(0, 0x0d)
+#define TAS2562_TDM_CFG8 TAS2562_REG(0, 0x0e)
+#define TAS2562_TDM_CFG9 TAS2562_REG(0, 0x0f)
+#define TAS2562_TDM_CFG10 TAS2562_REG(0, 0x10)
+#define TAS2562_TDM_DET TAS2562_REG(0, 0x11)
+#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_RESET BIT(0)
+
+#define TAS2562_MODE_MASK 0x3
+#define TAS2562_ACTIVE 0x0
+#define TAS2562_MUTE 0x1
+#define TAS2562_SHUTDOWN 0x2
+
+#define TAS2562_TDM_CFG1_RX_EDGE_MASK BIT(0)
+#define TAS2562_TDM_CFG1_RX_FALLING 1
+#define TAS2562_TDM_CFG1_RX_OFFSET_MASK GENMASK(4, 0)
+
+#define TAS2562_TDM_CFG0_RAMPRATE_MASK BIT(5)
+#define TAS2562_TDM_CFG0_RAMPRATE_44_1 BIT(5)
+#define TAS2562_TDM_CFG0_SAMPRATE_MASK GENMASK(3, 1)
+#define TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ 0x0
+#define TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ 0x1
+#define TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ 0x2
+#define TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ 0x3
+#define TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ 0x4
+#define TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ 0x5
+#define TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ 0x6
+
+#define TAS2562_TDM_CFG2_RIGHT_JUSTIFY BIT(6)
+
+#define TAS2562_TDM_CFG2_RXLEN_MASK GENMASK(1, 0)
+#define TAS2562_TDM_CFG2_RXLEN_16B 0x0
+#define TAS2562_TDM_CFG2_RXLEN_24B BIT(0)
+#define TAS2562_TDM_CFG2_RXLEN_32B BIT(1)
+
+#define TAS2562_TDM_CFG2_RXWLEN_MASK GENMASK(3, 2)
+#define TAS2562_TDM_CFG2_RXWLEN_16B 0x0
+#define TAS2562_TDM_CFG2_RXWLEN_20B BIT(2)
+#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_TDM_CFG5_VSNS_EN BIT(6)
+#define TAS2562_TDM_CFG5_VSNS_SLOT_MASK GENMASK(5, 0)
+
+#define TAS2562_TDM_CFG6_ISNS_EN BIT(6)
+#define TAS2562_TDM_CFG6_ISNS_SLOT_MASK GENMASK(5, 0)
+
+#endif /* __TAS2562_H__ */
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
new file mode 100644
index 000000000000..54c8135fe43c
--- /dev/null
+++ b/sound/soc/codecs/tas2770.c
@@ -0,0 +1,819 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2770 20-W Digital Input Mono Class-D
+// Audio Amplifier with Speaker I/V Sense
+//
+// Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
+// Author: Tracy Yi <tracy-yi@ti.com>
+// Frank Shi <shifu0704@thundersoft.com>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "tas2770.h"
+
+#define TAS2770_MDELAY 0xFFFFFFFE
+
+static void tas2770_reset(struct tas2770_priv *tas2770)
+{
+ if (tas2770->reset_gpio) {
+ gpiod_set_value_cansleep(tas2770->reset_gpio, 0);
+ msleep(20);
+ gpiod_set_value_cansleep(tas2770->reset_gpio, 1);
+ }
+ snd_soc_component_write(tas2770->component, TAS2770_SW_RST,
+ TAS2770_RST);
+}
+
+static int tas2770_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_component_update_bits(component,
+ TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_ACTIVE);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component,
+ TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_SHUTDOWN);
+ break;
+
+ default:
+ dev_err(tas2770->dev,
+ "wrong power level setting %d\n", level);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tas2770_codec_suspend(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_SHUTDOWN);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2770_codec_resume(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_ACTIVE);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#else
+#define tas2770_codec_suspend NULL
+#define tas2770_codec_resume NULL
+#endif
+
+static const char * const tas2770_ASI1_src[] = {
+ "I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2770_ASI1_src_enum, TAS2770_TDM_CFG_REG2,
+ 4, tas2770_ASI1_src);
+
+static const struct snd_kcontrol_new tas2770_asi1_mux =
+ SOC_DAPM_ENUM("ASI1 Source", tas2770_ASI1_src_enum);
+
+static int tas2770_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_MUTE);
+ if (ret)
+ goto end;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_SHUTDOWN);
+ if (ret)
+ goto end;
+ break;
+ default:
+ dev_err(tas2770->dev, "Not supported evevt\n");
+ return -EINVAL;
+ }
+
+end:
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new isense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 3, 1, 1);
+static const struct snd_kcontrol_new vsense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1);
+
+static const struct snd_soc_dapm_widget tas2770_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,
+ &tas2770_asi1_mux),
+ SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1,
+ &isense_switch),
+ SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1,
+ &vsense_switch),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas2770_audio_map[] = {
+ {"ASI1 Sel", "I2C offset", "ASI1"},
+ {"ASI1 Sel", "Left", "ASI1"},
+ {"ASI1 Sel", "Right", "ASI1"},
+ {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+ {"DAC", NULL, "ASI1 Sel"},
+ {"OUT", NULL, "DAC"},
+ {"ISENSE", "Switch", "IMON"},
+ {"VSENSE", "Switch", "VMON"},
+};
+
+static int tas2770_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ if (mute)
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_MUTE);
+ else
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_ACTIVE);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth)
+{
+ int ret;
+ struct snd_soc_component *component = tas2770->component;
+
+ switch (bitwidth) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXW_MASK,
+ TAS2770_TDM_CFG_REG2_RXW_16BITS);
+ tas2770->v_sense_slot = tas2770->i_sense_slot + 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXW_MASK,
+ TAS2770_TDM_CFG_REG2_RXW_24BITS);
+ tas2770->v_sense_slot = tas2770->i_sense_slot + 4;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXW_MASK,
+ TAS2770_TDM_CFG_REG2_RXW_32BITS);
+ tas2770->v_sense_slot = tas2770->i_sense_slot + 4;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ tas2770->channel_size = bitwidth;
+
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG5,
+ TAS2770_TDM_CFG_REG5_VSNS_MASK |
+ TAS2770_TDM_CFG_REG5_50_MASK,
+ TAS2770_TDM_CFG_REG5_VSNS_ENABLE |
+ tas2770->v_sense_slot);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG6,
+ TAS2770_TDM_CFG_REG6_ISNS_MASK |
+ TAS2770_TDM_CFG_REG6_50_MASK,
+ TAS2770_TDM_CFG_REG6_ISNS_ENABLE |
+ tas2770->i_sense_slot);
+
+end:
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate)
+{
+ int ret;
+ struct snd_soc_component *component = tas2770->component;
+
+ switch (samplerate) {
+ case 48000:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_48KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_44_1_48KHZ);
+ if (ret)
+ goto end;
+ break;
+ case 44100:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_44_1KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_44_1_48KHZ);
+ if (ret)
+ goto end;
+ break;
+ case 96000:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_48KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_88_2_96KHZ);
+ break;
+ case 88200:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_44_1KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_88_2_96KHZ);
+ break;
+ case 19200:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_48KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_176_4_192KHZ);
+ if (ret)
+ goto end;
+ break;
+ case 17640:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_44_1KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_176_4_192KHZ);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+end:
+ if (ret < 0)
+ return ret;
+
+ tas2770->sampling_rate = samplerate;
+ return 0;
+}
+
+static int tas2770_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 tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = tas2770_set_bitwidth(tas2770, params_format(params));
+ if (ret < 0)
+ goto end;
+
+
+ ret = tas2770_set_samplerate(tas2770, params_rate(params));
+
+end:
+ return ret;
+}
+
+static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
+ int ret;
+ struct snd_soc_component *component = dai->component;
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ dev_err(tas2770->dev, "ASI format master is not found\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_RSING;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_FALING;
+ break;
+ default:
+ dev_err(tas2770->dev, "ASI format Inverse is not found\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1,
+ TAS2770_TDM_CFG_REG1_RX_MASK,
+ asi_cfg_1);
+ if (ret < 0)
+ return ret;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ tdm_rx_start_slot = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ tdm_rx_start_slot = 0;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ tdm_rx_start_slot = 1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ tdm_rx_start_slot = 0;
+ break;
+ default:
+ dev_err(tas2770->dev,
+ "DAI Format is not found, fmt=0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1,
+ TAS2770_TDM_CFG_REG1_MASK,
+ (tdm_rx_start_slot << TAS2770_TDM_CFG_REG1_51_SHIFT));
+ if (ret < 0)
+ return ret;
+
+ tas2770->asi_format = fmt;
+
+ return 0;
+}
+
+static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int left_slot, right_slot;
+ int ret;
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ if (slots == 1) {
+ if (tx_mask != 1)
+ return -EINVAL;
+ left_slot = 0;
+ right_slot = 0;
+ } else {
+ left_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << left_slot);
+ if (tx_mask == 0) {
+ right_slot = left_slot;
+ } else {
+ right_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << right_slot);
+ }
+ }
+
+ if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
+ return -EINVAL;
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3,
+ TAS2770_TDM_CFG_REG3_30_MASK,
+ (left_slot << TAS2770_TDM_CFG_REG3_30_SHIFT));
+ if (ret < 0)
+ return ret;
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3,
+ TAS2770_TDM_CFG_REG3_RXS_MASK,
+ (right_slot << TAS2770_TDM_CFG_REG3_RXS_SHIFT));
+ if (ret < 0)
+ return ret;
+
+ switch (slot_width) {
+ case 16:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXS_MASK,
+ TAS2770_TDM_CFG_REG2_RXS_16BITS);
+ break;
+
+ case 24:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXS_MASK,
+ TAS2770_TDM_CFG_REG2_RXS_24BITS);
+ break;
+
+ case 32:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXS_MASK,
+ TAS2770_TDM_CFG_REG2_RXS_32BITS);
+ break;
+
+ case 0:
+ /* Do not change slot width */
+ ret = 0;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ tas2770->slot_width = slot_width;
+ return 0;
+}
+
+static struct snd_soc_dai_ops tas2770_dai_ops = {
+ .digital_mute = tas2770_mute,
+ .hw_params = tas2770_hw_params,
+ .set_fmt = tas2770_set_fmt,
+ .set_tdm_slot = tas2770_set_dai_tdm_slot,
+};
+
+#define TAS2770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TAS2770_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000\
+ )
+
+static struct snd_soc_dai_driver tas2770_dai_driver[] = {
+ {
+ .name = "tas2770 ASI1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASI1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = TAS2770_RATES,
+ .formats = TAS2770_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASI1 Capture",
+ .channels_min = 0,
+ .channels_max = 2,
+ .rates = TAS2770_RATES,
+ .formats = TAS2770_FORMATS,
+ },
+ .ops = &tas2770_dai_ops,
+ .symmetric_rates = 1,
+ },
+};
+
+static int tas2770_codec_probe(struct snd_soc_component *component)
+{
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+
+ tas2770->component = component;
+
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0);
+
+static const struct snd_kcontrol_new tas2770_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Playback Volume", TAS2770_PLAY_CFG_REG2,
+ 0, TAS2770_PLAY_CFG_REG2_VMAX, 1,
+ tas2770_playback_volume),
+ SOC_SINGLE_TLV("Amp Gain Volume", TAS2770_PLAY_CFG_REG0,
+ 0, 0x14, 0,
+ tas2770_digital_tlv),
+};
+
+static const struct snd_soc_component_driver soc_component_driver_tas2770 = {
+ .probe = tas2770_codec_probe,
+ .suspend = tas2770_codec_suspend,
+ .resume = tas2770_codec_resume,
+ .set_bias_level = tas2770_set_bias_level,
+ .controls = tas2770_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2770_snd_controls),
+ .dapm_widgets = tas2770_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2770_dapm_widgets),
+ .dapm_routes = tas2770_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2770_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static int tas2770_register_codec(struct tas2770_priv *tas2770)
+{
+ return devm_snd_soc_register_component(tas2770->dev,
+ &soc_component_driver_tas2770,
+ tas2770_dai_driver, ARRAY_SIZE(tas2770_dai_driver));
+}
+
+static const struct reg_default tas2770_reg_defaults[] = {
+ { TAS2770_PAGE, 0x00 },
+ { TAS2770_SW_RST, 0x00 },
+ { TAS2770_PWR_CTRL, 0x0e },
+ { TAS2770_PLAY_CFG_REG0, 0x10 },
+ { TAS2770_PLAY_CFG_REG1, 0x01 },
+ { TAS2770_PLAY_CFG_REG2, 0x00 },
+ { TAS2770_MSC_CFG_REG0, 0x07 },
+ { TAS2770_TDM_CFG_REG1, 0x02 },
+ { TAS2770_TDM_CFG_REG2, 0x0a },
+ { TAS2770_TDM_CFG_REG3, 0x10 },
+ { TAS2770_INT_MASK_REG0, 0xfc },
+ { TAS2770_INT_MASK_REG1, 0xb1 },
+ { TAS2770_INT_CFG, 0x05 },
+ { TAS2770_MISC_IRQ, 0x81 },
+ { TAS2770_CLK_CGF, 0x0c },
+
+};
+
+static bool tas2770_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS2770_PAGE: /* regmap implementation requires this */
+ case TAS2770_SW_RST: /* always clears after write */
+ case TAS2770_BO_PRV_REG0:/* has a self clearing bit */
+ case TAS2770_LVE_INT_REG0:
+ case TAS2770_LVE_INT_REG1:
+ case TAS2770_LAT_INT_REG0:/* Sticky interrupt flags */
+ case TAS2770_LAT_INT_REG1:/* Sticky interrupt flags */
+ case TAS2770_VBAT_MSB:
+ case TAS2770_VBAT_LSB:
+ case TAS2770_TEMP_MSB:
+ case TAS2770_TEMP_LSB:
+ return true;
+ }
+ return false;
+}
+
+static bool tas2770_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS2770_LVE_INT_REG0:
+ case TAS2770_LVE_INT_REG1:
+ case TAS2770_LAT_INT_REG0:
+ case TAS2770_LAT_INT_REG1:
+ case TAS2770_VBAT_MSB:
+ case TAS2770_VBAT_LSB:
+ case TAS2770_TEMP_MSB:
+ case TAS2770_TEMP_LSB:
+ case TAS2770_TDM_CLK_DETC:
+ case TAS2770_REV_AND_GPID:
+ return false;
+ }
+ return true;
+}
+
+static const struct regmap_range_cfg tas2770_regmap_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 1 * 128,
+ .selector_reg = TAS2770_PAGE,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tas2770_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = tas2770_writeable,
+ .volatile_reg = tas2770_volatile,
+ .reg_defaults = tas2770_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2770_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tas2770_regmap_ranges,
+ .num_ranges = ARRAY_SIZE(tas2770_regmap_ranges),
+ .max_register = 1 * 128,
+};
+
+static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770)
+{
+ int rc = 0;
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,asi-format",
+ &tas2770->asi_format);
+ if (rc) {
+ dev_err(tas2770->dev, "Looking up %s property failed %d\n",
+ "ti,asi-format", rc);
+ goto end;
+ }
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
+ &tas2770->i_sense_slot);
+ if (rc) {
+ dev_err(tas2770->dev, "Looking up %s property failed %d\n",
+ "ti,imon-slot-no", rc);
+ goto end;
+ }
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
+ &tas2770->v_sense_slot);
+ if (rc) {
+ dev_err(tas2770->dev, "Looking up %s property failed %d\n",
+ "ti,vmon-slot-no", rc);
+ goto end;
+ }
+
+end:
+ return rc;
+}
+
+static int tas2770_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tas2770_priv *tas2770;
+ int result;
+
+ tas2770 = devm_kzalloc(&client->dev,
+ sizeof(struct tas2770_priv), GFP_KERNEL);
+ if (!tas2770)
+ return -ENOMEM;
+ tas2770->dev = &client->dev;
+
+ i2c_set_clientdata(client, tas2770);
+ dev_set_drvdata(&client->dev, tas2770);
+ tas2770->power_state = TAS2770_POWER_SHUTDOWN;
+
+ tas2770->regmap = devm_regmap_init_i2c(client, &tas2770_i2c_regmap);
+ if (IS_ERR(tas2770->regmap)) {
+ result = PTR_ERR(tas2770->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ result);
+ goto end;
+ }
+
+ if (client->dev.of_node) {
+ result = tas2770_parse_dt(&client->dev, tas2770);
+ if (result) {
+ dev_err(tas2770->dev, "%s: Failed to parse devicetree\n",
+ __func__);
+ goto end;
+ }
+ }
+
+ tas2770->reset_gpio = devm_gpiod_get_optional(tas2770->dev,
+ "reset-gpio",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2770->reset_gpio)) {
+ if (PTR_ERR(tas2770->reset_gpio) == -EPROBE_DEFER) {
+ tas2770->reset_gpio = NULL;
+ return -EPROBE_DEFER;
+ }
+ }
+
+ tas2770->channel_size = 0;
+ tas2770->slot_width = 0;
+
+ tas2770_reset(tas2770);
+
+ result = tas2770_register_codec(tas2770);
+ if (result)
+ dev_err(tas2770->dev, "Register codec failed.\n");
+
+end:
+ return result;
+}
+
+static int tas2770_i2c_remove(struct i2c_client *client)
+{
+ pm_runtime_disable(&client->dev);
+ return 0;
+}
+
+
+static const struct i2c_device_id tas2770_i2c_id[] = {
+ { "tas2770", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2770_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2770_of_match[] = {
+ { .compatible = "ti,tas2770" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tas2770_of_match);
+#endif
+
+static struct i2c_driver tas2770_i2c_driver = {
+ .driver = {
+ .name = "tas2770",
+ .of_match_table = of_match_ptr(tas2770_of_match),
+ },
+ .probe = tas2770_i2c_probe,
+ .remove = tas2770_i2c_remove,
+ .id_table = tas2770_i2c_id,
+};
+
+module_i2c_driver(tas2770_i2c_driver);
+
+MODULE_AUTHOR("Shi Fu <shifu0704@thundersoft.com>");
+MODULE_DESCRIPTION("TAS2770 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h
new file mode 100644
index 000000000000..cbb858369fe6
--- /dev/null
+++ b/sound/soc/codecs/tas2770.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC TAS2770 codec driver
+ *
+ * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
+ */
+#ifndef __TAS2770__
+#define __TAS2770__
+
+/* Book Control Register (available in page0 of each book) */
+#define TAS2770_BOOKCTL_PAGE 0
+#define TAS2770_BOOKCTL_REG 127
+#define TAS2770_REG(page, reg) ((page * 128) + reg)
+ /* Page */
+#define TAS2770_PAGE TAS2770_REG(0X0, 0x00)
+#define TAS2770_PAGE_PAGE_MASK 255
+ /* Software Reset */
+#define TAS2770_SW_RST TAS2770_REG(0X0, 0x01)
+#define TAS2770_RST BIT(0)
+ /* Power Control */
+#define TAS2770_PWR_CTRL TAS2770_REG(0X0, 0x02)
+#define TAS2770_PWR_CTRL_MASK 0x3
+#define TAS2770_PWR_CTRL_ACTIVE 0x0
+#define TAS2770_PWR_CTRL_MUTE BIT(0)
+#define TAS2770_PWR_CTRL_SHUTDOWN 0x2
+ /* Playback Configuration Reg0 */
+#define TAS2770_PLAY_CFG_REG0 TAS2770_REG(0X0, 0x03)
+ /* Playback Configuration Reg1 */
+#define TAS2770_PLAY_CFG_REG1 TAS2770_REG(0X0, 0x04)
+ /* Playback Configuration Reg2 */
+#define TAS2770_PLAY_CFG_REG2 TAS2770_REG(0X0, 0x05)
+#define TAS2770_PLAY_CFG_REG2_VMAX 0xc9
+ /* Misc Configuration Reg0 */
+#define TAS2770_MSC_CFG_REG0 TAS2770_REG(0X0, 0x07)
+ /* TDM Configuration Reg0 */
+#define TAS2770_TDM_CFG_REG0 TAS2770_REG(0X0, 0x0A)
+#define TAS2770_TDM_CFG_REG0_SMP_MASK BIT(5)
+#define TAS2770_TDM_CFG_REG0_SMP_48KHZ 0x0
+#define TAS2770_TDM_CFG_REG0_SMP_44_1KHZ BIT(5)
+#define TAS2770_TDM_CFG_REG0_31_MASK 0xe
+#define TAS2770_TDM_CFG_REG0_31_44_1_48KHZ 0x6
+#define TAS2770_TDM_CFG_REG0_31_88_2_96KHZ 0x8
+#define TAS2770_TDM_CFG_REG0_31_176_4_192KHZ 0xa
+ /* TDM Configuration Reg1 */
+#define TAS2770_TDM_CFG_REG1 TAS2770_REG(0X0, 0x0B)
+#define TAS2770_TDM_CFG_REG1_MASK 0x3e
+#define TAS2770_TDM_CFG_REG1_51_SHIFT 1
+#define TAS2770_TDM_CFG_REG1_RX_MASK BIT(0)
+#define TAS2770_TDM_CFG_REG1_RX_RSING 0x0
+#define TAS2770_TDM_CFG_REG1_RX_FALING BIT(0)
+ /* TDM Configuration Reg2 */
+#define TAS2770_TDM_CFG_REG2 TAS2770_REG(0X0, 0x0C)
+#define TAS2770_TDM_CFG_REG2_RXW_MASK 0xc
+#define TAS2770_TDM_CFG_REG2_RXW_16BITS 0x0
+#define TAS2770_TDM_CFG_REG2_RXW_24BITS 0x8
+#define TAS2770_TDM_CFG_REG2_RXW_32BITS 0xc
+#define TAS2770_TDM_CFG_REG2_RXS_MASK 0x3
+#define TAS2770_TDM_CFG_REG2_RXS_16BITS 0x0
+#define TAS2770_TDM_CFG_REG2_RXS_24BITS BIT(0)
+#define TAS2770_TDM_CFG_REG2_RXS_32BITS 0x2
+ /* TDM Configuration Reg3 */
+#define TAS2770_TDM_CFG_REG3 TAS2770_REG(0X0, 0x0D)
+#define TAS2770_TDM_CFG_REG3_RXS_MASK 0xf0
+#define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4
+#define TAS2770_TDM_CFG_REG3_30_MASK 0xf
+#define TAS2770_TDM_CFG_REG3_30_SHIFT 0
+ /* TDM Configuration Reg5 */
+#define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F)
+#define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6)
+#define TAS2770_TDM_CFG_REG5_VSNS_ENABLE BIT(6)
+#define TAS2770_TDM_CFG_REG5_50_MASK 0x3f
+ /* TDM Configuration Reg6 */
+#define TAS2770_TDM_CFG_REG6 TAS2770_REG(0X0, 0x10)
+#define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6)
+#define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6)
+#define TAS2770_TDM_CFG_REG6_50_MASK 0x3f
+ /* Brown Out Prevention Reg0 */
+#define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B)
+ /* Interrupt MASK Reg0 */
+#define TAS2770_INT_MASK_REG0 TAS2770_REG(0X0, 0x20)
+#define TAS2770_INT_REG0_DEFAULT 0xfc
+#define TAS2770_INT_MASK_REG0_DISABLE 0xff
+ /* Interrupt MASK Reg1 */
+#define TAS2770_INT_MASK_REG1 TAS2770_REG(0X0, 0x21)
+#define TAS2770_INT_REG1_DEFAULT 0xb1
+#define TAS2770_INT_MASK_REG1_DISABLE 0xff
+ /* Live-Interrupt Reg0 */
+#define TAS2770_LVE_INT_REG0 TAS2770_REG(0X0, 0x22)
+ /* Live-Interrupt Reg1 */
+#define TAS2770_LVE_INT_REG1 TAS2770_REG(0X0, 0x23)
+ /* Latched-Interrupt Reg0 */
+#define TAS2770_LAT_INT_REG0 TAS2770_REG(0X0, 0x24)
+#define TAS2770_LAT_INT_REG0_OCE_FLG BIT(1)
+#define TAS2770_LAT_INT_REG0_OTE_FLG BIT(0)
+ /* Latched-Interrupt Reg1 */
+#define TAS2770_LAT_INT_REG1 TAS2770_REG(0X0, 0x25)
+#define TAS2770_LAT_INT_REG1_VBA_TOV BIT(3)
+#define TAS2770_LAT_INT_REG1_VBA_TUV BIT(2)
+#define TAS2770_LAT_INT_REG1_BOUT_FLG BIT(1)
+ /* VBAT MSB */
+#define TAS2770_VBAT_MSB TAS2770_REG(0X0, 0x27)
+ /* VBAT LSB */
+#define TAS2770_VBAT_LSB TAS2770_REG(0X0, 0x28)
+ /* TEMP MSB */
+#define TAS2770_TEMP_MSB TAS2770_REG(0X0, 0x29)
+ /* TEMP LSB */
+#define TAS2770_TEMP_LSB TAS2770_REG(0X0, 0x2A)
+ /* Interrupt Configuration */
+#define TAS2770_INT_CFG TAS2770_REG(0X0, 0x30)
+ /* Misc IRQ */
+#define TAS2770_MISC_IRQ TAS2770_REG(0X0, 0x32)
+ /* Clock Configuration */
+#define TAS2770_CLK_CGF TAS2770_REG(0X0, 0x3C)
+ /* TDM Clock detection monitor */
+#define TAS2770_TDM_CLK_DETC TAS2770_REG(0X0, 0x77)
+ /* Revision and PG ID */
+#define TAS2770_REV_AND_GPID TAS2770_REG(0X0, 0x7D)
+
+#define TAS2770_POWER_ACTIVE 0
+#define TAS2770_POWER_MUTE 1
+#define TAS2770_POWER_SHUTDOWN 2
+#define ERROR_OVER_CURRENT 0x0000001
+#define ERROR_DIE_OVERTEMP 0x0000002
+#define ERROR_OVER_VOLTAGE 0x0000004
+#define ERROR_UNDER_VOLTAGE 0x0000008
+#define ERROR_BROWNOUT 0x0000010
+#define ERROR_CLASSD_PWR 0x0000020
+
+struct tas2770_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ int power_state;
+ int asi_format;
+ struct gpio_desc *reset_gpio;
+ int sampling_rate;
+ int channel_size;
+ int slot_width;
+ int v_sense_slot;
+ int i_sense_slot;
+};
+
+#endif /* __TAS2770__ */
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index df627a08def9..f6f19fdc72f5 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -171,6 +171,7 @@ struct aic31xx_priv {
int rate_div_line;
bool master_dapm_route_applied;
int irq;
+ u8 ocmv; /* output common-mode voltage */
};
struct aic31xx_rate_divs {
@@ -1312,6 +1313,11 @@ static int aic31xx_codec_probe(struct snd_soc_component *component)
if (ret)
return ret;
+ /* set output common-mode voltage */
+ snd_soc_component_update_bits(component, AIC31XX_HPDRIVER,
+ AIC31XX_HPD_OCMV_MASK,
+ aic31xx->ocmv << AIC31XX_HPD_OCMV_SHIFT);
+
return 0;
}
@@ -1501,6 +1507,43 @@ exit:
return IRQ_NONE;
}
+static void aic31xx_configure_ocmv(struct aic31xx_priv *priv)
+{
+ struct device *dev = priv->dev;
+ int dvdd, avdd;
+ u32 value;
+
+ if (dev->fwnode &&
+ fwnode_property_read_u32(dev->fwnode, "ai31xx-ocmv", &value)) {
+ /* OCMV setting is forced by DT */
+ if (value <= 3) {
+ priv->ocmv = value;
+ return;
+ }
+ }
+
+ avdd = regulator_get_voltage(priv->supplies[3].consumer);
+ dvdd = regulator_get_voltage(priv->supplies[5].consumer);
+
+ if (avdd > 3600000 || dvdd > 1950000) {
+ dev_warn(dev,
+ "Too high supply voltage(s) AVDD: %d, DVDD: %d\n",
+ avdd, dvdd);
+ } else if (avdd == 3600000 && dvdd == 1950000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_8V;
+ } else if (avdd >= 3300000 && dvdd >= 1800000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_65V;
+ } else if (avdd >= 3000000 && dvdd >= 1650000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_5V;
+ } else if (avdd >= 2700000 && dvdd >= 1525000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_35V;
+ } else {
+ dev_warn(dev,
+ "Invalid supply voltage(s) AVDD: %d, DVDD: %d\n",
+ avdd, dvdd);
+ }
+}
+
static int aic31xx_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1570,6 +1613,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ aic31xx_configure_ocmv(aic31xx);
+
if (aic31xx->irq > 0) {
regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
AIC31XX_GPIO1_FUNC_MASK,
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index cb024955c978..83a8c7604cc3 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -232,6 +232,14 @@ struct aic31xx_pdata {
#define AIC31XX_HSD_HP 0x01
#define AIC31XX_HSD_HS 0x03
+/* AIC31XX_HPDRIVER */
+#define AIC31XX_HPD_OCMV_MASK GENMASK(4, 3)
+#define AIC31XX_HPD_OCMV_SHIFT 3
+#define AIC31XX_HPD_OCMV_1_35V 0x0
+#define AIC31XX_HPD_OCMV_1_5V 0x1
+#define AIC31XX_HPD_OCMV_1_65V 0x2
+#define AIC31XX_HPD_OCMV_1_8V 0x3
+
/* AIC31XX_MICBIAS */
#define AIC31XX_MICBIAS_MASK GENMASK(1, 0)
#define AIC31XX_MICBIAS_SHIFT 0
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 68165de1c8de..b4e9a6c73f90 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -573,6 +573,9 @@ static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
struct clk *pll;
pll = devm_clk_get(component->dev, "pll");
+ if (IS_ERR(pll))
+ return PTR_ERR(pll);
+
mclk = clk_get_parent(pll);
return clk_set_rate(mclk, freq);
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index f318403133e9..f11ffa28683b 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -2837,11 +2837,11 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w,
TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x10);
snd_soc_component_update_bits(comp, dec_cfg_reg, 0x08, 0x00);
- if (hpf_coff_freq != CF_MIN_3DB_150HZ) {
- snd_soc_component_update_bits(comp, dec_cfg_reg,
- TX_HPF_CUT_OFF_FREQ_MASK,
- hpf_coff_freq << 5);
- }
+ if (hpf_coff_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ hpf_coff_freq << 5);
+ }
break;
case SND_SOC_DAPM_POST_PMD:
snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x00);
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index cf64e109c658..7b087d94141b 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2410,6 +2410,8 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
err_pm_runtime:
pm_runtime_disable(&i2c->dev);
+ if (i2c->irq)
+ free_irq(i2c->irq, wm2200);
err_reset:
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
@@ -2426,12 +2428,15 @@ static int wm2200_i2c_remove(struct i2c_client *i2c)
{
struct wm2200_priv *wm2200 = i2c_get_clientdata(i2c);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
if (wm2200->pdata.ldo_ena)
gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
return 0;
}
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 4af0e519e623..91cc63c5a51f 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2617,6 +2617,7 @@ static int wm5100_i2c_probe(struct i2c_client *i2c,
return ret;
err_reset:
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
@@ -2640,6 +2641,7 @@ static int wm5100_i2c_remove(struct i2c_client *i2c)
{
struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 9e8c564f6e9c..7d7ea15d73e0 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -1410,34 +1410,6 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-
-static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
- unsigned int freq, int dir)
-{
- struct snd_soc_component *component = dai->component;
- struct wm8904_priv *priv = snd_soc_component_get_drvdata(component);
-
- switch (clk_id) {
- case WM8904_CLK_MCLK:
- priv->sysclk_src = clk_id;
- priv->mclk_rate = freq;
- break;
-
- case WM8904_CLK_FLL:
- priv->sysclk_src = clk_id;
- break;
-
- default:
- return -EINVAL;
- }
-
- dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
-
- wm8904_configure_clocking(component);
-
- return 0;
-}
-
static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
@@ -1824,6 +1796,50 @@ out:
return 0;
}
+static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8904_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned long mclk_freq;
+ int ret;
+
+ switch (clk_id) {
+ case WM8904_CLK_AUTO:
+ mclk_freq = clk_get_rate(priv->mclk);
+ /* enable FLL if a different sysclk is desired */
+ if (mclk_freq != freq) {
+ priv->sysclk_src = WM8904_CLK_FLL;
+ ret = wm8904_set_fll(dai, WM8904_FLL_MCLK,
+ WM8904_FLL_MCLK,
+ mclk_freq, freq);
+ if (ret)
+ return ret;
+ break;
+ }
+ clk_id = WM8904_CLK_MCLK;
+ /* fallthrough */
+
+ case WM8904_CLK_MCLK:
+ priv->sysclk_src = clk_id;
+ priv->mclk_rate = freq;
+ break;
+
+ case WM8904_CLK_FLL:
+ priv->sysclk_src = clk_id;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ wm8904_configure_clocking(component);
+
+ return 0;
+}
+
static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_component *component = codec_dai->component;
diff --git a/sound/soc/codecs/wm8904.h b/sound/soc/codecs/wm8904.h
index c1bca52f9927..de6340446b1f 100644
--- a/sound/soc/codecs/wm8904.h
+++ b/sound/soc/codecs/wm8904.h
@@ -10,6 +10,7 @@
#ifndef _WM8904_H
#define _WM8904_H
+#define WM8904_CLK_AUTO 0
#define WM8904_CLK_MCLK 1
#define WM8904_CLK_FLL 2
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index 18535b326680..ca42445b649d 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -25,6 +25,8 @@
#include <linux/mfd/wm8994/pdata.h>
#include <linux/mfd/wm8994/gpio.h>
+#include <asm/unaligned.h>
+
#include "wm8994.h"
#define WM_FW_BLOCK_INFO 0xff
@@ -58,18 +60,15 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
}
if (memcmp(fw->data, "WMFW", 4) != 0) {
- memcpy(&data32, fw->data, sizeof(data32));
- data32 = be32_to_cpu(data32);
+ data32 = get_unaligned_be32(fw->data);
dev_err(component->dev, "%s: firmware has bad file magic %08x\n",
name, data32);
goto err;
}
- memcpy(&data32, fw->data + 4, sizeof(data32));
- len = be32_to_cpu(data32);
+ len = get_unaligned_be32(fw->data + 4);
+ data32 = get_unaligned_be32(fw->data + 8);
- memcpy(&data32, fw->data + 8, sizeof(data32));
- data32 = be32_to_cpu(data32);
if ((data32 >> 24) & 0xff) {
dev_err(component->dev, "%s: unsupported firmware version %d\n",
name, (data32 >> 24) & 0xff);
@@ -87,9 +86,8 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
}
if (check) {
- memcpy(&data64, fw->data + 24, sizeof(u64));
- dev_info(component->dev, "%s timestamp %llx\n",
- name, be64_to_cpu(data64));
+ data64 = get_unaligned_be64(fw->data + 24);
+ dev_info(component->dev, "%s timestamp %llx\n", name, data64);
} else {
snd_soc_component_write(component, 0x102, 0x2);
snd_soc_component_write(component, 0x900, 0x2);
@@ -104,8 +102,7 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
goto err;
}
- memcpy(&data32, data + 4, sizeof(data32));
- block_len = be32_to_cpu(data32);
+ block_len = get_unaligned_be32(data + 4);
if (block_len + 8 > len) {
dev_err(component->dev, "%zd byte block longer than file\n",
block_len);
@@ -116,8 +113,7 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
goto err;
}
- memcpy(&data32, data, sizeof(data32));
- data32 = be32_to_cpu(data32);
+ data32 = get_unaligned_be32(data);
switch ((data32 >> 24) & 0xff) {
case WM_FW_BLOCK_INFO:
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index d5fb7f5dd551..15ce64a48a87 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -167,12 +167,12 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif)
switch (wm8994->sysclk[aif]) {
case WM8994_SYSCLK_MCLK1:
- rate = wm8994->mclk[0];
+ rate = wm8994->mclk_rate[0];
break;
case WM8994_SYSCLK_MCLK2:
reg1 |= 0x8;
- rate = wm8994->mclk[1];
+ rate = wm8994->mclk_rate[1];
break;
case WM8994_SYSCLK_FLL1:
@@ -1038,6 +1038,45 @@ static bool wm8994_check_class_w_digital(struct snd_soc_component *component)
return true;
}
+static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enable)
+{
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
+ unsigned int offset, val, clk_idx;
+ int ret;
+
+ if (aif)
+ offset = 4;
+ else
+ offset = 0;
+
+ val = snd_soc_component_read32(component, WM8994_AIF1_CLOCKING_1 + offset);
+ val &= WM8994_AIF1CLK_SRC_MASK;
+
+ switch (val) {
+ case 0:
+ clk_idx = WM8994_MCLK1;
+ break;
+ case 1:
+ clk_idx = WM8994_MCLK2;
+ break;
+ default:
+ return 0;
+ }
+
+ if (enable) {
+ ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable MCLK%d\n",
+ clk_idx);
+ return ret;
+ }
+ } else {
+ clk_disable_unprepare(wm8994->mclk[clk_idx].clk);
+ }
+
+ return 0;
+}
+
static int aif1clk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -1045,7 +1084,7 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int mask = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA;
- int i;
+ int ret, i;
int dac;
int adc;
int val;
@@ -1061,6 +1100,10 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ ret = aif_mclk_set(component, 0, true);
+ if (ret < 0)
+ return ret;
+
/* Don't enable timeslot 2 if not in use */
if (wm8994->channels[0] <= 2)
mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
@@ -1133,6 +1176,12 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
break;
}
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ aif_mclk_set(component, 0, false);
+ break;
+ }
+
return 0;
}
@@ -1140,13 +1189,17 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
- int i;
+ int ret, i;
int dac;
int adc;
int val;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ ret = aif_mclk_set(component, 1, true);
+ if (ret < 0)
+ return ret;
+
val = snd_soc_component_read32(component, WM8994_AIF2_CONTROL_1);
if ((val & WM8994_AIF2ADCL_SRC) &&
(val & WM8994_AIF2ADCR_SRC))
@@ -1218,6 +1271,12 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
break;
}
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ aif_mclk_set(component, 1, false);
+ break;
+ }
+
return 0;
}
@@ -1623,10 +1682,10 @@ SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = {
SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, aif1clk_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
- SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, aif2clk_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
- SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
@@ -2141,6 +2200,7 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
u16 reg, clk1, aif_reg, aif_src;
unsigned long timeout;
bool was_enabled;
+ struct clk *mclk;
switch (id) {
case WM8994_FLL1:
@@ -2216,6 +2276,27 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset,
WM8994_FLL1_ENA, 0);
+ /* Disable MCLK if needed before we possibly change to new clock parent */
+ if (was_enabled) {
+ reg = snd_soc_component_read32(component, WM8994_FLL1_CONTROL_5
+ + reg_offset);
+ reg = ((reg & WM8994_FLL1_REFCLK_SRC_MASK)
+ >> WM8994_FLL1_REFCLK_SRC_SHIFT) + 1;
+
+ switch (reg) {
+ case WM8994_FLL_SRC_MCLK1:
+ mclk = wm8994->mclk[WM8994_MCLK1].clk;
+ break;
+ case WM8994_FLL_SRC_MCLK2:
+ mclk = wm8994->mclk[WM8994_MCLK2].clk;
+ break;
+ default:
+ mclk = NULL;
+ }
+
+ clk_disable_unprepare(mclk);
+ }
+
if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK &&
freq_in == freq_out && freq_out) {
dev_dbg(component->dev, "Bypassing FLL%d\n", id + 1);
@@ -2260,10 +2341,29 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
/* Clear any pending completion from a previous failure */
try_wait_for_completion(&wm8994->fll_locked[id]);
+ switch (src) {
+ case WM8994_FLL_SRC_MCLK1:
+ mclk = wm8994->mclk[WM8994_MCLK1].clk;
+ break;
+ case WM8994_FLL_SRC_MCLK2:
+ mclk = wm8994->mclk[WM8994_MCLK2].clk;
+ break;
+ default:
+ mclk = NULL;
+ }
+
/* Enable (with fractional mode if required) */
if (freq_out) {
+ ret = clk_prepare_enable(mclk);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable MCLK for FLL%d\n",
+ id + 1);
+ return ret;
+ }
+
/* Enable VMID if we need it */
if (!was_enabled) {
+
active_reference(component);
switch (control->type) {
@@ -2372,12 +2472,29 @@ static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src,
return _wm8994_set_fll(dai->component, id, src, freq_in, freq_out);
}
+static int wm8994_set_mclk_rate(struct wm8994_priv *wm8994, unsigned int id,
+ unsigned int *freq)
+{
+ int ret;
+
+ if (!wm8994->mclk[id].clk || *freq == wm8994->mclk_rate[id])
+ return 0;
+
+ ret = clk_set_rate(wm8994->mclk[id].clk, *freq);
+ if (ret < 0)
+ return ret;
+
+ *freq = clk_get_rate(wm8994->mclk[id].clk);
+
+ return 0;
+}
+
static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_component *component = dai->component;
struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
- int i;
+ int ret, i;
switch (dai->id) {
case 1:
@@ -2392,7 +2509,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
switch (clk_id) {
case WM8994_SYSCLK_MCLK1:
wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1;
- wm8994->mclk[0] = freq;
+
+ ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
+ if (ret < 0)
+ return ret;
+
+ wm8994->mclk_rate[0] = freq;
dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n",
dai->id, freq);
break;
@@ -2400,7 +2522,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
case WM8994_SYSCLK_MCLK2:
/* TODO: Set GPIO AF */
wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2;
- wm8994->mclk[1] = freq;
+
+ ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
+ if (ret < 0)
+ return ret;
+
+ wm8994->mclk_rate[1] = freq;
dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
dai->id, freq);
break;
@@ -4456,6 +4583,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8994 = {
static int wm8994_probe(struct platform_device *pdev)
{
struct wm8994_priv *wm8994;
+ int ret;
wm8994 = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_priv),
GFP_KERNEL);
@@ -4467,6 +4595,16 @@ static int wm8994_probe(struct platform_device *pdev)
wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
+ wm8994->mclk[WM8994_MCLK1].id = "MCLK1";
+ wm8994->mclk[WM8994_MCLK2].id = "MCLK2";
+
+ ret = devm_clk_bulk_get_optional(pdev->dev.parent, ARRAY_SIZE(wm8994->mclk),
+ wm8994->mclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to get clocks: %d\n", ret);
+ return ret;
+ }
+
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 1d6f2abe1c11..41c4b126114d 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -6,6 +6,7 @@
#ifndef _WM8994_H
#define _WM8994_H
+#include <linux/clk.h>
#include <sound/soc.h>
#include <linux/firmware.h>
#include <linux/completion.h>
@@ -14,6 +15,12 @@
#include "wm_hubs.h"
+enum {
+ WM8994_MCLK1,
+ WM8994_MCLK2,
+ WM8994_NUM_MCLK
+};
+
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1
#define WM8994_SYSCLK_MCLK2 2
@@ -73,9 +80,10 @@ struct wm8994;
struct wm8994_priv {
struct wm_hubs_data hubs;
struct wm8994 *wm8994;
+ struct clk_bulk_data mclk[WM8994_NUM_MCLK];
int sysclk[2];
int sysclk_rate[2];
- int mclk[2];
+ int mclk_rate[2];
int aifclk[2];
int aifdiv[2];
int channels[2];
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 9b8bb7bbe945..2a9b610f6d43 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -599,6 +599,9 @@ struct wm_coeff_ctl_ops {
struct wm_coeff_ctl {
const char *name;
const char *fw_name;
+ /* Subname is needed to match with firmware */
+ const char *subname;
+ unsigned int subname_len;
struct wm_adsp_alg_region alg_region;
struct wm_coeff_ctl_ops ops;
struct wm_adsp *dsp;
@@ -1399,6 +1402,7 @@ static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
{
kfree(ctl->cache);
kfree(ctl->name);
+ kfree(ctl->subname);
kfree(ctl);
}
@@ -1472,6 +1476,15 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
ret = -ENOMEM;
goto err_ctl;
}
+ if (subname) {
+ ctl->subname_len = subname_len;
+ ctl->subname = kmemdup(subname,
+ strlen(subname) + 1, GFP_KERNEL);
+ if (!ctl->subname) {
+ ret = -ENOMEM;
+ goto err_ctl_name;
+ }
+ }
ctl->enabled = 1;
ctl->set = 0;
ctl->ops.xget = wm_coeff_get;
@@ -1485,7 +1498,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
if (!ctl->cache) {
ret = -ENOMEM;
- goto err_ctl_name;
+ goto err_ctl_subname;
}
list_add(&ctl->list, &dsp->ctl_list);
@@ -1508,6 +1521,8 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
err_ctl_cache:
kfree(ctl->cache);
+err_ctl_subname:
+ kfree(ctl->subname);
err_ctl_name:
kfree(ctl->name);
err_ctl:
@@ -1995,6 +2010,70 @@ out:
return ret;
}
+/*
+ * Find wm_coeff_ctl with input name as its subname
+ * If not found, return NULL
+ */
+static struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp,
+ const char *name, int type,
+ unsigned int alg)
+{
+ struct wm_coeff_ctl *pos, *rslt = NULL;
+
+ list_for_each_entry(pos, &dsp->ctl_list, list) {
+ if (!pos->subname)
+ continue;
+ if (strncmp(pos->subname, name, pos->subname_len) == 0 &&
+ pos->alg_region.alg == alg &&
+ pos->alg_region.type == type) {
+ rslt = pos;
+ break;
+ }
+ }
+
+ return rslt;
+}
+
+int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len)
+{
+ struct wm_coeff_ctl *ctl;
+ struct snd_kcontrol *kcontrol;
+ int ret;
+
+ ctl = wm_adsp_get_ctl(dsp, name, type, alg);
+ if (!ctl)
+ return -EINVAL;
+
+ if (len > ctl->len)
+ return -EINVAL;
+
+ ret = wm_coeff_write_control(ctl, buf, len);
+
+ kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl->name);
+ snd_ctl_notify(dsp->component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);
+
+int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len)
+{
+ struct wm_coeff_ctl *ctl;
+
+ ctl = wm_adsp_get_ctl(dsp, name, type, alg);
+ if (!ctl)
+ return -EINVAL;
+
+ if (len > ctl->len)
+ return -EINVAL;
+
+ return wm_coeff_read_control(ctl, buf, len);
+}
+EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);
+
static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *alg_region)
{
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index aa634ef6c9f5..4c481cf20275 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -201,5 +201,9 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp);
int wm_adsp_compr_copy(struct snd_compr_stream *stream,
char __user *buf, size_t count);
+int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len);
+int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len);
#endif
diff --git a/sound/soc/dwc/dwc-pcm.c b/sound/soc/dwc/dwc-pcm.c
index a9ae91c4597f..de6fcc808832 100644
--- a/sound/soc/dwc/dwc-pcm.c
+++ b/sound/soc/dwc/dwc-pcm.c
@@ -135,7 +135,8 @@ void dw_pcm_pop_rx(struct dw_i2s_dev *dev)
dw_pcm_transfer(dev, false);
}
-static int dw_pcm_open(struct snd_pcm_substream *substream)
+static int dw_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -148,14 +149,16 @@ static int dw_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int dw_pcm_close(struct snd_pcm_substream *substream)
+static int dw_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
synchronize_rcu();
return 0;
}
-static int dw_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+static int dw_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct dw_i2s_dev *dev = runtime->private_data;
@@ -192,12 +195,14 @@ static int dw_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int dw_pcm_hw_free(struct snd_pcm_substream *substream)
+static int dw_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
-static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int dw_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct dw_i2s_dev *dev = runtime->private_data;
@@ -231,7 +236,8 @@ static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
-static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t dw_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct dw_i2s_dev *dev = runtime->private_data;
@@ -245,7 +251,8 @@ static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)
return pos < runtime->buffer_size ? pos : 0;
}
-static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int dw_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
size_t size = dw_pcm_hardware.buffer_bytes_max;
@@ -255,25 +262,22 @@ static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static void dw_pcm_free(struct snd_pcm *pcm)
+static void dw_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
-static const struct snd_pcm_ops dw_pcm_ops = {
- .open = dw_pcm_open,
- .close = dw_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = dw_pcm_hw_params,
- .hw_free = dw_pcm_hw_free,
- .trigger = dw_pcm_trigger,
- .pointer = dw_pcm_pointer,
-};
-
static const struct snd_soc_component_driver dw_pcm_component = {
- .pcm_new = dw_pcm_new,
- .pcm_free = dw_pcm_free,
- .ops = &dw_pcm_ops,
+ .open = dw_pcm_open,
+ .close = dw_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = dw_pcm_hw_params,
+ .hw_free = dw_pcm_hw_free,
+ .trigger = dw_pcm_trigger,
+ .pointer = dw_pcm_pointer,
+ .pcm_construct = dw_pcm_new,
+ .pcm_destruct = dw_pcm_free,
};
int dw_pcm_register(struct platform_device *pdev)
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index aa99c008a925..65e8cd4be930 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -25,6 +25,16 @@ config SND_SOC_FSL_SAI
This option is only useful for out-of-tree drivers since
in-tree drivers select it automatically.
+config SND_SOC_FSL_MQS
+ tristate "Medium Quality Sound (MQS) module support"
+ depends on SND_SOC_FSL_SAI
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add Medium Quality Sound (MQS)
+ support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
+
config SND_SOC_FSL_AUDMIX
tristate "Audio Mixer (AUDMIX) module support"
select REGMAP_MMIO
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index c0dd04422fe9..8cde88c72d93 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -23,6 +23,7 @@ snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-micfil-objs := fsl_micfil.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
+snd-soc-fsl-mqs-objs := fsl_mqs.o
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
@@ -33,6 +34,7 @@ obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
+obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
# MPC5200 Platform Support
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index cfa40ef6b1ca..a3cfceea7d2f 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -115,7 +115,7 @@ static void fsl_asrc_sel_proc(int inrate, int outrate,
* within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
* while pair A and pair C are comparatively independent.
*/
-static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
+int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
{
enum asrc_pair_index index = ASRC_INVALID_PAIR;
struct fsl_asrc *asrc_priv = pair->asrc_priv;
@@ -158,7 +158,7 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
*
* It clears the resource from asrc_priv and releases the occupied channels.
*/
-static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
+void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc_priv = pair->asrc_priv;
enum asrc_pair_index index = pair->index;
@@ -259,14 +259,24 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
* It configures those ASRC registers according to a configuration instance
* of struct asrc_config which includes in/output sample rate, width, channel
* and clock settings.
+ *
+ * Note:
+ * The ideal ratio configuration can work with a flexible clock rate setting.
+ * Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC.
+ * For a regular audio playback, the clock rate should not be slower than an
+ * clock rate aligning with the output sample rate; For a use case requiring
+ * faster conversion, set use_ideal_rate to have the faster speed.
*/
-static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
+static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
{
struct asrc_config *config = pair->config;
struct fsl_asrc *asrc_priv = pair->asrc_priv;
enum asrc_pair_index index = pair->index;
+ enum asrc_word_width input_word_width;
+ enum asrc_word_width output_word_width;
u32 inrate, outrate, indiv, outdiv;
- u32 clk_index[2], div[2];
+ u32 clk_index[2], div[2], rem[2];
+ u64 clk_rate;
int in, out, channels;
int pre_proc, post_proc;
struct clk *clk;
@@ -283,9 +293,32 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
return -EINVAL;
}
- /* Validate output width */
- if (config->output_word_width == ASRC_WIDTH_8_BIT) {
- pair_err("does not support 8bit width output\n");
+ switch (snd_pcm_format_width(config->input_format)) {
+ case 8:
+ input_word_width = ASRC_WIDTH_8_BIT;
+ break;
+ case 16:
+ input_word_width = ASRC_WIDTH_16_BIT;
+ break;
+ case 24:
+ input_word_width = ASRC_WIDTH_24_BIT;
+ break;
+ default:
+ pair_err("does not support this input format, %d\n",
+ config->input_format);
+ return -EINVAL;
+ }
+
+ switch (snd_pcm_format_width(config->output_format)) {
+ case 16:
+ output_word_width = ASRC_WIDTH_16_BIT;
+ break;
+ case 24:
+ output_word_width = ASRC_WIDTH_24_BIT;
+ break;
+ default:
+ pair_err("does not support this output format, %d\n",
+ config->output_format);
return -EINVAL;
}
@@ -326,27 +359,42 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
/* We only have output clock for ideal ratio mode */
clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
- div[IN] = clk_get_rate(clk) / inrate;
- if (div[IN] == 0) {
+ clk_rate = clk_get_rate(clk);
+ rem[IN] = do_div(clk_rate, inrate);
+ div[IN] = (u32)clk_rate;
+
+ /*
+ * The divider range is [1, 1024], defined by the hardware. For non-
+ * ideal ratio configuration, clock rate has to be strictly aligned
+ * with the sample rate. For ideal ratio configuration, clock rates
+ * only result in different converting speeds. So remainder does not
+ * matter, as long as we keep the divider within its valid range.
+ */
+ if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) {
pair_err("failed to support input sample rate %dHz by asrck_%x\n",
inrate, clk_index[ideal ? OUT : IN]);
return -EINVAL;
}
- clk = asrc_priv->asrck_clk[clk_index[OUT]];
+ div[IN] = min_t(u32, 1024, div[IN]);
- /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
- if (ideal)
- div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
+ clk = asrc_priv->asrck_clk[clk_index[OUT]];
+ clk_rate = clk_get_rate(clk);
+ if (ideal && use_ideal_rate)
+ rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
else
- div[OUT] = clk_get_rate(clk) / outrate;
+ rem[OUT] = do_div(clk_rate, outrate);
+ div[OUT] = clk_rate;
- if (div[OUT] == 0) {
+ /* Output divider has the same limitation as the input one */
+ if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) {
pair_err("failed to support output sample rate %dHz by asrck_%x\n",
outrate, clk_index[OUT]);
return -EINVAL;
}
+ div[OUT] = min_t(u32, 1024, div[OUT]);
+
/* Set the channel number */
channels = config->channel_num;
@@ -383,8 +431,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
/* Implement word_width configurations */
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
- ASRMCR1i_OW16(config->output_word_width) |
- ASRMCR1i_IWD(config->input_word_width));
+ ASRMCR1i_OW16(output_word_width) |
+ ASRMCR1i_IWD(input_word_width));
/* Enable BUFFER STALL */
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
@@ -497,13 +545,13 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
- int width = params_width(params);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
struct asrc_config config;
- int word_width, ret;
+ snd_pcm_format_t format;
+ int ret;
ret = fsl_asrc_request_pair(channels, pair);
if (ret) {
@@ -513,15 +561,10 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
pair->config = &config;
- if (width == 16)
- width = ASRC_WIDTH_16_BIT;
- else
- width = ASRC_WIDTH_24_BIT;
-
if (asrc_priv->asrc_width == 16)
- word_width = ASRC_WIDTH_16_BIT;
+ format = SNDRV_PCM_FORMAT_S16_LE;
else
- word_width = ASRC_WIDTH_24_BIT;
+ format = SNDRV_PCM_FORMAT_S24_LE;
config.pair = pair->index;
config.channel_num = channels;
@@ -529,18 +572,18 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
config.outclk = OUTCLK_ASRCK1_CLK;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- config.input_word_width = width;
- config.output_word_width = word_width;
+ config.input_format = params_format(params);
+ config.output_format = format;
config.input_sample_rate = rate;
config.output_sample_rate = asrc_priv->asrc_rate;
} else {
- config.input_word_width = word_width;
- config.output_word_width = width;
+ config.input_format = format;
+ config.output_format = params_format(params);
config.input_sample_rate = asrc_priv->asrc_rate;
config.output_sample_rate = rate;
}
- ret = fsl_asrc_config_pair(pair);
+ ret = fsl_asrc_config_pair(pair, false);
if (ret) {
dev_err(dai->dev, "fail to config asrc pair\n");
return ret;
@@ -604,7 +647,7 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S20_3LE)
+ SNDRV_PCM_FMTBIT_S24_3LE)
static struct snd_soc_dai_driver fsl_asrc_dai = {
.probe = fsl_asrc_dai_probe,
@@ -615,7 +658,8 @@ static struct snd_soc_dai_driver fsl_asrc_dai = {
.rate_min = 5512,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_ASRC_FORMATS,
+ .formats = FSL_ASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_S8,
},
.capture = {
.stream_name = "ASRC-Capture",
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index c60075112570..2b57e8c53728 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -342,8 +342,8 @@ struct asrc_config {
unsigned int dma_buffer_size;
unsigned int input_sample_rate;
unsigned int output_sample_rate;
- enum asrc_word_width input_word_width;
- enum asrc_word_width output_word_width;
+ snd_pcm_format_t input_format;
+ snd_pcm_format_t output_format;
enum asrc_inclk inclk;
enum asrc_outclk outclk;
};
@@ -462,4 +462,7 @@ struct fsl_asrc {
#define DRV_NAME "fsl-asrc-dai"
extern struct snd_soc_component_driver fsl_asrc_component;
struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
+int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair);
+void fsl_asrc_release_pair(struct fsl_asrc_pair *pair);
+
#endif /* _FSL_ASRC_H */
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index 01052a0808b0..d6146de9acd2 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -16,13 +16,11 @@
#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
-static const struct snd_pcm_hardware snd_imx_hardware = {
+static struct snd_pcm_hardware snd_imx_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = 65535, /* Limited by SDMA engine */
@@ -54,13 +52,12 @@ static void fsl_asrc_dma_complete(void *arg)
snd_pcm_period_elapsed(substream);
}
-static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
+static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream,
+ struct snd_soc_component *component)
{
u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
unsigned long flags = DMA_CTRL_ACK;
@@ -97,7 +94,8 @@ static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
return 0;
}
-static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+static int fsl_asrc_dma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@@ -107,7 +105,7 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = fsl_asrc_dma_prepare_and_submit(substream);
+ ret = fsl_asrc_dma_prepare_and_submit(substream, component);
if (ret)
return ret;
dma_async_issue_pending(pair->dma_chan[IN]);
@@ -126,7 +124,8 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
-static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
+static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
@@ -134,7 +133,6 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
struct fsl_asrc *asrc_priv = pair->asrc_priv;
@@ -249,7 +247,8 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
+static int fsl_asrc_dma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@@ -268,14 +267,27 @@ static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
+static int fsl_asrc_dma_startup(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ struct snd_dmaengine_dai_dma_data *dma_data;
struct device *dev = component->dev;
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
struct fsl_asrc_pair *pair;
+ struct dma_chan *tmp_chan = NULL;
+ u8 dir = tx ? OUT : IN;
+ bool release_pair = true;
+ int ret = 0;
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(dev, "failed to set pcm hw params periods\n");
+ return ret;
+ }
pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
if (!pair)
@@ -285,14 +297,54 @@ static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
runtime->private_data = pair;
- snd_pcm_hw_constraint_integer(substream->runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
+ /* Request a dummy pair, which will be released later.
+ * Request pair function needs channel num as input, for this
+ * dummy pair, we just request "1" channel temporarily.
+ */
+ ret = fsl_asrc_request_pair(1, pair);
+ if (ret < 0) {
+ dev_err(dev, "failed to request asrc pair\n");
+ goto req_pair_err;
+ }
+
+ /* Request a dummy dma channel, which will be released later. */
+ tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
+ if (!tmp_chan) {
+ dev_err(dev, "failed to get dma channel\n");
+ ret = -EINVAL;
+ goto dma_chan_err;
+ }
+
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+ /* Refine the snd_imx_hardware according to caps of DMA. */
+ ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream,
+ dma_data,
+ &snd_imx_hardware,
+ tmp_chan);
+ if (ret < 0) {
+ dev_err(dev, "failed to refine runtime hwparams\n");
+ goto out;
+ }
+
+ release_pair = false;
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
- return 0;
+out:
+ dma_release_channel(tmp_chan);
+
+dma_chan_err:
+ fsl_asrc_release_pair(pair);
+
+req_pair_err:
+ if (release_pair)
+ kfree(pair);
+
+ return ret;
}
-static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
+static int fsl_asrc_dma_shutdown(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@@ -311,7 +363,9 @@ static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
return 0;
}
-static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t
+fsl_asrc_dma_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@@ -319,17 +373,8 @@ static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *subs
return bytes_to_frames(substream->runtime, pair->pos);
}
-static const struct snd_pcm_ops fsl_asrc_dma_pcm_ops = {
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = fsl_asrc_dma_hw_params,
- .hw_free = fsl_asrc_dma_hw_free,
- .trigger = fsl_asrc_dma_trigger,
- .open = fsl_asrc_dma_startup,
- .close = fsl_asrc_dma_shutdown,
- .pointer = fsl_asrc_dma_pcm_pointer,
-};
-
-static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm_substream *substream;
@@ -364,7 +409,8 @@ err:
return ret;
}
-static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
+static void fsl_asrc_dma_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
int i;
@@ -382,8 +428,14 @@ static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
struct snd_soc_component_driver fsl_asrc_component = {
.name = DRV_NAME,
- .ops = &fsl_asrc_dma_pcm_ops,
- .pcm_new = fsl_asrc_dma_pcm_new,
- .pcm_free = fsl_asrc_dma_pcm_free,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = fsl_asrc_dma_hw_params,
+ .hw_free = fsl_asrc_dma_hw_free,
+ .trigger = fsl_asrc_dma_trigger,
+ .open = fsl_asrc_dma_startup,
+ .close = fsl_asrc_dma_shutdown,
+ .pointer = fsl_asrc_dma_pcm_pointer,
+ .pcm_construct = fsl_asrc_dma_pcm_new,
+ .pcm_destruct = fsl_asrc_dma_pcm_free,
};
EXPORT_SYMBOL_GPL(fsl_asrc_component);
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
index c7e4e9757dce..a1db1bce330f 100644
--- a/sound/soc/fsl/fsl_audmix.c
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -286,6 +286,7 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned long lock_flags;
/* Capture stream shall not be handled */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -295,12 +296,16 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irqsave(&priv->lock, lock_flags);
priv->tdms |= BIT(dai->driver->id);
+ spin_unlock_irqrestore(&priv->lock, lock_flags);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock_irqsave(&priv->lock, lock_flags);
priv->tdms &= ~BIT(dai->driver->id);
+ spin_unlock_irqrestore(&priv->lock, lock_flags);
break;
default:
return -EINVAL;
@@ -491,6 +496,7 @@ static int fsl_audmix_probe(struct platform_device *pdev)
return PTR_ERR(priv->ipg_clk);
}
+ spin_lock_init(&priv->lock);
platform_set_drvdata(pdev, priv);
pm_runtime_enable(dev);
diff --git a/sound/soc/fsl/fsl_audmix.h b/sound/soc/fsl/fsl_audmix.h
index 7812ffec45c5..479f05695d53 100644
--- a/sound/soc/fsl/fsl_audmix.h
+++ b/sound/soc/fsl/fsl_audmix.h
@@ -96,6 +96,7 @@ struct fsl_audmix {
struct platform_device *pdev;
struct regmap *regmap;
struct clk *ipg_clk;
+ spinlock_t lock; /* Protect tdms */
u8 tdms;
};
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index e22508301412..2868c4f97cb2 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -201,8 +201,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
struct fsl_dma_private *dma_private = dev_id;
struct snd_pcm_substream *substream = dma_private->substream;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
- struct device *dev = component->dev;
+ struct device *dev = rtd->dev;
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
irqreturn_t ret = IRQ_NONE;
u32 sr, sr2 = 0;
@@ -280,7 +279,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
* Regardless of where the memory is actually allocated, since the device can
* technically DMA to any 36-bit address, we do need to set the DMA mask to 36.
*/
-static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
+static int fsl_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -380,11 +380,10 @@ static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
* buffer, which is what ALSA expects. We're just dividing it into
* contiguous parts, and creating a link descriptor for each one.
*/
-static int fsl_dma_open(struct snd_pcm_substream *substream)
+static int fsl_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct dma_object *dma =
container_of(component->driver, struct dma_object, dai);
@@ -533,13 +532,12 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
* and 8 bytes at a time). So we do not support packed 24-bit samples.
* 24-bit data must be padded to 32 bits.
*/
-static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+static int fsl_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
/* Number of bits per sample */
@@ -698,12 +696,11 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
* The base address of the buffer is stored in the source_addr field of the
* first link descriptor.
*/
-static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t fsl_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
dma_addr_t position;
@@ -763,7 +760,8 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
*
* This function can be called multiple times.
*/
-static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
+static int fsl_dma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
@@ -796,12 +794,11 @@ static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
/**
* fsl_dma_close: close the stream.
*/
-static int fsl_dma_close(struct snd_pcm_substream *substream)
+static int fsl_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct dma_object *dma =
container_of(component->driver, struct dma_object, dai);
@@ -824,7 +821,8 @@ static int fsl_dma_close(struct snd_pcm_substream *substream)
/*
* Remove this PCM driver.
*/
-static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm)
+static void fsl_dma_free_dma_buffers(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
unsigned int i;
@@ -872,15 +870,6 @@ static struct device_node *find_ssi_node(struct device_node *dma_channel_np)
return NULL;
}
-static const struct snd_pcm_ops fsl_dma_ops = {
- .open = fsl_dma_open,
- .close = fsl_dma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = fsl_dma_hw_params,
- .hw_free = fsl_dma_hw_free,
- .pointer = fsl_dma_pointer,
-};
-
static int fsl_soc_dma_probe(struct platform_device *pdev)
{
struct dma_object *dma;
@@ -912,9 +901,14 @@ static int fsl_soc_dma_probe(struct platform_device *pdev)
}
dma->dai.name = DRV_NAME;
- dma->dai.ops = &fsl_dma_ops;
- dma->dai.pcm_new = fsl_dma_new;
- dma->dai.pcm_free = fsl_dma_free_dma_buffers;
+ dma->dai.open = fsl_dma_open;
+ dma->dai.close = fsl_dma_close;
+ dma->dai.ioctl = snd_soc_pcm_lib_ioctl;
+ dma->dai.hw_params = fsl_dma_hw_params;
+ dma->dai.hw_free = fsl_dma_hw_free;
+ dma->dai.pointer = fsl_dma_pointer;
+ dma->dai.pcm_construct = fsl_dma_new;
+ dma->dai.pcm_destruct = fsl_dma_free_dma_buffers;
/* Store the SSI-specific information that we need */
dma->ssi_stx_phys = res.start + REG_SSI_STX0;
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index a78e4ab478df..c7a49d03463a 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -33,6 +33,7 @@
* @fsysclk: system clock source to derive HCK, SCK and FS
* @spbaclk: SPBA clock (optional, depending on SoC design)
* @task: tasklet to handle the reset operation
+ * @lock: spin lock between hw_reset() and trigger()
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @slots: number of slots
@@ -56,6 +57,7 @@ struct fsl_esai {
struct clk *fsysclk;
struct clk *spbaclk;
struct tasklet_struct task;
+ spinlock_t lock; /* Protect hw_reset and trigger */
u32 fifo_depth;
u32 slot_width;
u32 slots;
@@ -676,8 +678,10 @@ static void fsl_esai_hw_reset(unsigned long arg)
{
struct fsl_esai *esai_priv = (struct fsl_esai *)arg;
bool tx = true, rx = false, enabled[2];
+ unsigned long lock_flags;
u32 tfcr, rfcr;
+ spin_lock_irqsave(&esai_priv->lock, lock_flags);
/* Save the registers */
regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
@@ -715,6 +719,8 @@ static void fsl_esai_hw_reset(unsigned long arg)
fsl_esai_trigger_start(esai_priv, tx);
if (enabled[rx])
fsl_esai_trigger_start(esai_priv, rx);
+
+ spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
}
static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -722,6 +728,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned long lock_flags;
esai_priv->channels[tx] = substream->runtime->channels;
@@ -729,12 +736,16 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irqsave(&esai_priv->lock, lock_flags);
fsl_esai_trigger_start(esai_priv, tx);
+ spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock_irqsave(&esai_priv->lock, lock_flags);
fsl_esai_trigger_stop(esai_priv, tx);
+ spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
break;
default:
return -EINVAL;
@@ -1002,6 +1013,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, esai_priv);
+ spin_lock_init(&esai_priv->lock);
ret = fsl_esai_hw_init(esai_priv);
if (ret)
return ret;
diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c
new file mode 100644
index 000000000000..0c813a45bba7
--- /dev/null
+++ b/sound/soc/fsl/fsl_mqs.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC IMX MQS driver
+//
+// Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
+// Copyright 2019 NXP
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#define REG_MQS_CTRL 0x00
+
+#define MQS_EN_MASK (0x1 << 28)
+#define MQS_EN_SHIFT (28)
+#define MQS_SW_RST_MASK (0x1 << 24)
+#define MQS_SW_RST_SHIFT (24)
+#define MQS_OVERSAMPLE_MASK (0x1 << 20)
+#define MQS_OVERSAMPLE_SHIFT (20)
+#define MQS_CLK_DIV_MASK (0xFF << 0)
+#define MQS_CLK_DIV_SHIFT (0)
+
+/* codec private data */
+struct fsl_mqs {
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *ipg;
+
+ unsigned int reg_iomuxc_gpr2;
+ unsigned int reg_mqs_ctrl;
+ bool use_gpr;
+};
+
+#define FSL_MQS_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define FSL_MQS_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+static int fsl_mqs_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 fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
+ unsigned long mclk_rate;
+ int div, res;
+ int lrclk;
+
+ mclk_rate = clk_get_rate(mqs_priv->mclk);
+ lrclk = params_rate(params);
+
+ /*
+ * mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate;
+ * if repeat_rate is 8, mqs can achieve better quality.
+ * oversample rate is fix to 32 currently.
+ */
+ div = mclk_rate / (32 * lrclk * 2 * 8);
+ res = mclk_rate % (32 * lrclk * 2 * 8);
+
+ if (res == 0 && div > 0 && div <= 256) {
+ if (mqs_priv->use_gpr) {
+ regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
+ IMX6SX_GPR2_MQS_CLK_DIV_MASK,
+ (div - 1) << IMX6SX_GPR2_MQS_CLK_DIV_SHIFT);
+ regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
+ IMX6SX_GPR2_MQS_OVERSAMPLE_MASK, 0);
+ } else {
+ regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
+ MQS_CLK_DIV_MASK,
+ (div - 1) << MQS_CLK_DIV_SHIFT);
+ regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
+ MQS_OVERSAMPLE_MASK, 0);
+ }
+ } else {
+ dev_err(component->dev, "can't get proper divider\n");
+ }
+
+ return 0;
+}
+
+static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ /* Only LEFT_J & SLAVE mode is supported. */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_mqs_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
+
+ if (mqs_priv->use_gpr)
+ regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
+ IMX6SX_GPR2_MQS_EN_MASK,
+ 1 << IMX6SX_GPR2_MQS_EN_SHIFT);
+ else
+ regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
+ MQS_EN_MASK,
+ 1 << MQS_EN_SHIFT);
+ return 0;
+}
+
+static void fsl_mqs_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
+
+ if (mqs_priv->use_gpr)
+ regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
+ IMX6SX_GPR2_MQS_EN_MASK, 0);
+ else
+ regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
+ MQS_EN_MASK, 0);
+}
+
+static const struct snd_soc_component_driver soc_codec_fsl_mqs = {
+ .idle_bias_on = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static const struct snd_soc_dai_ops fsl_mqs_dai_ops = {
+ .startup = fsl_mqs_startup,
+ .shutdown = fsl_mqs_shutdown,
+ .hw_params = fsl_mqs_hw_params,
+ .set_fmt = fsl_mqs_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver fsl_mqs_dai = {
+ .name = "fsl-mqs-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_MQS_RATES,
+ .formats = FSL_MQS_FORMATS,
+ },
+ .ops = &fsl_mqs_dai_ops,
+};
+
+static const struct regmap_config fsl_mqs_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = REG_MQS_CTRL,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int fsl_mqs_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *gpr_np = NULL;
+ struct fsl_mqs *mqs_priv;
+ void __iomem *regs;
+ int ret;
+
+ mqs_priv = devm_kzalloc(&pdev->dev, sizeof(*mqs_priv), GFP_KERNEL);
+ if (!mqs_priv)
+ return -ENOMEM;
+
+ /* On i.MX6sx the MQS control register is in GPR domain
+ * But in i.MX8QM/i.MX8QXP the control register is moved
+ * to its own domain.
+ */
+ if (of_device_is_compatible(np, "fsl,imx8qm-mqs"))
+ mqs_priv->use_gpr = false;
+ else
+ mqs_priv->use_gpr = true;
+
+ if (mqs_priv->use_gpr) {
+ gpr_np = of_parse_phandle(np, "gpr", 0);
+ if (!gpr_np) {
+ dev_err(&pdev->dev, "failed to get gpr node by phandle\n");
+ return -EINVAL;
+ }
+
+ mqs_priv->regmap = syscon_node_to_regmap(gpr_np);
+ if (IS_ERR(mqs_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to get gpr regmap\n");
+ ret = PTR_ERR(mqs_priv->regmap);
+ goto err_free_gpr_np;
+ }
+ } else {
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ mqs_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ "core",
+ regs,
+ &fsl_mqs_regmap_config);
+ if (IS_ERR(mqs_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap: %ld\n",
+ PTR_ERR(mqs_priv->regmap));
+ return PTR_ERR(mqs_priv->regmap);
+ }
+
+ mqs_priv->ipg = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(mqs_priv->ipg)) {
+ dev_err(&pdev->dev, "failed to get the clock: %ld\n",
+ PTR_ERR(mqs_priv->ipg));
+ return PTR_ERR(mqs_priv->ipg);
+ }
+ }
+
+ mqs_priv->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(mqs_priv->mclk)) {
+ dev_err(&pdev->dev, "failed to get the clock: %ld\n",
+ PTR_ERR(mqs_priv->mclk));
+ ret = PTR_ERR(mqs_priv->mclk);
+ goto err_free_gpr_np;
+ }
+
+ dev_set_drvdata(&pdev->dev, mqs_priv);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_fsl_mqs,
+ &fsl_mqs_dai, 1);
+ if (ret)
+ goto err_free_gpr_np;
+ return 0;
+
+err_free_gpr_np:
+ of_node_put(gpr_np);
+
+ return ret;
+}
+
+static int fsl_mqs_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int fsl_mqs_runtime_resume(struct device *dev)
+{
+ struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
+
+ if (mqs_priv->ipg)
+ clk_prepare_enable(mqs_priv->ipg);
+
+ if (mqs_priv->mclk)
+ clk_prepare_enable(mqs_priv->mclk);
+
+ if (mqs_priv->use_gpr)
+ regmap_write(mqs_priv->regmap, IOMUXC_GPR2,
+ mqs_priv->reg_iomuxc_gpr2);
+ else
+ regmap_write(mqs_priv->regmap, REG_MQS_CTRL,
+ mqs_priv->reg_mqs_ctrl);
+ return 0;
+}
+
+static int fsl_mqs_runtime_suspend(struct device *dev)
+{
+ struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
+
+ if (mqs_priv->use_gpr)
+ regmap_read(mqs_priv->regmap, IOMUXC_GPR2,
+ &mqs_priv->reg_iomuxc_gpr2);
+ else
+ regmap_read(mqs_priv->regmap, REG_MQS_CTRL,
+ &mqs_priv->reg_mqs_ctrl);
+
+ if (mqs_priv->mclk)
+ clk_disable_unprepare(mqs_priv->mclk);
+
+ if (mqs_priv->ipg)
+ clk_disable_unprepare(mqs_priv->ipg);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops fsl_mqs_pm_ops = {
+ SET_RUNTIME_PM_OPS(fsl_mqs_runtime_suspend,
+ fsl_mqs_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct of_device_id fsl_mqs_dt_ids[] = {
+ { .compatible = "fsl,imx8qm-mqs", },
+ { .compatible = "fsl,imx6sx-mqs", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids);
+
+static struct platform_driver fsl_mqs_driver = {
+ .probe = fsl_mqs_probe,
+ .remove = fsl_mqs_remove,
+ .driver = {
+ .name = "fsl-mqs",
+ .of_match_table = fsl_mqs_dt_ids,
+ .pm = &fsl_mqs_pm_ops,
+ },
+};
+
+module_platform_driver(fsl_mqs_driver);
+
+MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
+MODULE_DESCRIPTION("MQS codec driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform: fsl-mqs");
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index c49aea4fba56..08131d147983 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -69,8 +69,9 @@ static struct fiq_handler fh = {
.name = DRV_NAME,
};
-static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int snd_imx_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -85,7 +86,8 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
+static int snd_imx_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -104,7 +106,8 @@ static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
static int imx_pcm_fiq;
-static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int snd_imx_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -141,7 +144,9 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
-static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t
+snd_imx_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -165,7 +170,8 @@ static const struct snd_pcm_hardware snd_imx_hardware = {
.fifo_size = 0,
};
-static int snd_imx_open(struct snd_pcm_substream *substream)
+static int snd_imx_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd;
@@ -194,7 +200,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_imx_close(struct snd_pcm_substream *substream)
+static int snd_imx_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -206,8 +213,9 @@ static int snd_imx_close(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static int snd_imx_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
@@ -222,17 +230,6 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
return ret;
}
-static const struct snd_pcm_ops imx_pcm_ops = {
- .open = snd_imx_open,
- .close = snd_imx_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_imx_pcm_hw_params,
- .prepare = snd_imx_pcm_prepare,
- .trigger = snd_imx_pcm_trigger,
- .pointer = snd_imx_pcm_pointer,
- .mmap = snd_imx_pcm_mmap,
-};
-
static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
@@ -279,7 +276,8 @@ static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
static int ssi_irq;
-static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
+static int snd_imx_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
struct snd_pcm_substream *substream;
@@ -329,7 +327,8 @@ static void imx_pcm_free(struct snd_pcm *pcm)
}
}
-static void imx_pcm_fiq_free(struct snd_pcm *pcm)
+static void snd_imx_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
mxc_set_irq_fiq(ssi_irq, 0);
release_fiq(&fh);
@@ -337,9 +336,16 @@ static void imx_pcm_fiq_free(struct snd_pcm *pcm)
}
static const struct snd_soc_component_driver imx_soc_component_fiq = {
- .ops = &imx_pcm_ops,
- .pcm_new = imx_pcm_fiq_new,
- .pcm_free = imx_pcm_fiq_free,
+ .open = snd_imx_open,
+ .close = snd_imx_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = snd_imx_pcm_hw_params,
+ .prepare = snd_imx_pcm_prepare,
+ .trigger = snd_imx_pcm_trigger,
+ .pointer = snd_imx_pcm_pointer,
+ .mmap = snd_imx_pcm_mmap,
+ .pcm_construct = snd_imx_pcm_new,
+ .pcm_destruct = snd_imx_pcm_free,
};
int imx_pcm_fiq_init(struct platform_device *pdev,
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index ccf9301889fe..5237ac96b756 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -98,7 +98,8 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream)
return IRQ_HANDLED;
}
-static int psc_dma_hw_free(struct snd_pcm_substream *substream)
+static int psc_dma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
@@ -110,7 +111,8 @@ static int psc_dma_hw_free(struct snd_pcm_substream *substream)
* This function is called by ALSA to start, stop, pause, and resume the DMA
* transfer of data.
*/
-static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+static int psc_dma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
@@ -210,7 +212,8 @@ static const struct snd_pcm_hardware psc_dma_hardware = {
.fifo_size = 512,
};
-static int psc_dma_open(struct snd_pcm_substream *substream)
+static int psc_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -238,7 +241,8 @@ static int psc_dma_open(struct snd_pcm_substream *substream)
return 0;
}
-static int psc_dma_close(struct snd_pcm_substream *substream)
+static int psc_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
@@ -263,7 +267,8 @@ static int psc_dma_close(struct snd_pcm_substream *substream)
}
static snd_pcm_uframes_t
-psc_dma_pointer(struct snd_pcm_substream *substream)
+psc_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
@@ -280,29 +285,19 @@ psc_dma_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(substream->runtime, count);
}
-static int
-psc_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int psc_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
return 0;
}
-static const struct snd_pcm_ops psc_dma_ops = {
- .open = psc_dma_open,
- .close = psc_dma_close,
- .hw_free = psc_dma_hw_free,
- .ioctl = snd_pcm_lib_ioctl,
- .pointer = psc_dma_pointer,
- .trigger = psc_dma_trigger,
- .hw_params = psc_dma_hw_params,
-};
-
-static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
+static int psc_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
size_t size = psc_dma_hardware.buffer_bytes_max;
@@ -341,10 +336,10 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
return -ENOMEM;
}
-static void psc_dma_free(struct snd_pcm *pcm)
+static void psc_dma_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_pcm_substream *substream;
int stream;
@@ -362,9 +357,15 @@ static void psc_dma_free(struct snd_pcm *pcm)
static const struct snd_soc_component_driver mpc5200_audio_dma_component = {
.name = DRV_NAME,
- .ops = &psc_dma_ops,
- .pcm_new = &psc_dma_new,
- .pcm_free = &psc_dma_free,
+ .open = psc_dma_open,
+ .close = psc_dma_close,
+ .hw_free = psc_dma_hw_free,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .pointer = psc_dma_pointer,
+ .trigger = psc_dma_trigger,
+ .hw_params = psc_dma_hw_params,
+ .pcm_construct = psc_dma_new,
+ .pcm_destruct = psc_dma_free,
};
int mpc5200_audio_dma_create(struct platform_device *op)
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 6007e6305735..9ad35d9940fe 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -232,7 +232,7 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
if (li->cpu) {
int is_single_links = 0;
- /* BE is dummy */
+ /* Codec is dummy */
codecs->of_node = NULL;
codecs->dai_name = "snd-soc-dummy-dai";
codecs->name = "snd-soc-dummy";
@@ -263,7 +263,7 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
} else {
struct snd_soc_codec_conf *cconf;
- /* FE is dummy */
+ /* CPU is dummy */
cpus->of_node = NULL;
cpus->dai_name = "snd-soc-dummy-dai";
cpus->name = "snd-soc-dummy";
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index fc9c753db8dd..10b82bf043d1 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -149,7 +149,7 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
if (li->cpu) {
int is_single_links = 0;
- /* BE is dummy */
+ /* Codec is dummy */
codecs->of_node = NULL;
codecs->dai_name = "snd-soc-dummy-dai";
codecs->name = "snd-soc-dummy";
@@ -179,7 +179,7 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
} else {
struct snd_soc_codec_conf *cconf;
- /* FE is dummy */
+ /* CPU is dummy */
cpus->of_node = NULL;
cpus->dai_name = "snd-soc-dummy-dai";
cpus->name = "snd-soc-dummy";
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 01c99750212a..79b227613108 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -59,10 +59,13 @@ config SND_SOC_INTEL_HASWELL
If you have a Intel Haswell or Broadwell platform connected to
an I2S codec, then enable this option by saying Y or m. This is
typically used for Chromebooks. This is a recommended option.
+ This option is mutually exclusive with the SOF support on
+ Broadwell. If you want to enable SOF on Broadwell, you need to
+ deselect this option first.
config SND_SOC_INTEL_BAYTRAIL
tristate "Baytrail (legacy) Platforms"
- depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n
+ depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n && SND_SOC_SOF_BAYTRAIL=n
select SND_SOC_INTEL_SST
select SND_SOC_INTEL_SST_ACPI
select SND_SOC_INTEL_SST_FIRMWARE
@@ -101,6 +104,9 @@ config SND_SST_ATOM_HIFI2_PLATFORM_ACPI
If you have a Intel Baytrail or Cherrytrail platform with an I2S
codec, then enable this option by saying Y or m. This is a
recommended option
+ This option is mutually exclusive with the SOF support on
+ Baytrail/Cherrytrail. If you want to enable SOF on
+ Baytrail/Cherrytrail, you need to deselect this option first.
config SND_SOC_INTEL_SKYLAKE
tristate "All Skylake/SST Platforms"
@@ -203,9 +209,12 @@ config SND_SOC_INTEL_SKYLAKE_SSP_CLK
config SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC
bool "HDAudio codec support"
help
- If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/
- GeminiLake or CannonLake platform with an HDaudio codec
- then enable this option by saying Y
+ This option broke audio on Linus' Skylake laptop in December 2018
+ and the race conditions during the probe were not fixed since.
+ This option is DEPRECATED, all HDaudio codec support needs
+ to be handled by the SOF driver.
+ Distributions should not enable this option and there are no known
+ users of this capability.
config SND_SOC_INTEL_SKYLAKE_COMMON
tristate
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 8cc3cc363eb0..47e3d1943d7e 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -586,7 +586,8 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
},
};
-static int sst_platform_open(struct snd_pcm_substream *substream)
+static int sst_soc_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
@@ -598,15 +599,15 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
return 0;
}
-static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
+static int sst_soc_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
int ret_val = 0, str_id;
struct sst_runtime_stream *stream;
int status;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n");
+ dev_dbg(rtd->dev, "%s called\n", __func__);
if (substream->pcm->internal)
return 0;
stream = substream->runtime->private_data;
@@ -646,8 +647,8 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
}
-static snd_pcm_uframes_t sst_platform_pcm_pointer
- (struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream;
int ret_val, status;
@@ -668,14 +669,8 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer
return str_info->buffer_ptr;
}
-static const struct snd_pcm_ops sst_platform_ops = {
- .open = sst_platform_open,
- .ioctl = snd_pcm_lib_ioctl,
- .trigger = sst_platform_pcm_trigger,
- .pointer = sst_platform_pcm_pointer,
-};
-
-static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int sst_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
@@ -709,9 +704,12 @@ static const struct snd_soc_component_driver sst_soc_platform_drv = {
.name = DRV_NAME,
.probe = sst_soc_probe,
.remove = sst_soc_remove,
- .ops = &sst_platform_ops,
+ .open = sst_soc_open,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .trigger = sst_soc_trigger,
+ .pointer = sst_soc_pointer,
.compr_ops = &sst_platform_compr_ops,
- .pcm_new = sst_pcm_new,
+ .pcm_construct = sst_soc_pcm_new,
};
static int sst_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/intel/baytrail/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c
index 54f2ee3010ee..1d780fcc448c 100644
--- a/sound/soc/intel/baytrail/sst-baytrail-pcm.c
+++ b/sound/soc/intel/baytrail/sst-baytrail-pcm.c
@@ -58,11 +58,11 @@ struct sst_byt_priv_data {
};
/* this may get called several times by oss emulation */
-static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
+static int sst_byt_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
@@ -121,7 +121,8 @@ static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
+static int sst_byt_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -164,10 +165,10 @@ static void sst_byt_pcm_work(struct work_struct *work)
sst_byt_pcm_restore_stream_context(pcm_data->substream);
}
-static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int sst_byt_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
@@ -228,11 +229,11 @@ static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
return pos;
}
-static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_soc_component *component,
+ 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 = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
@@ -241,10 +242,10 @@ static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream
return bytes_to_frames(runtime, pcm_data->hw_ptr);
}
-static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
+static int sst_byt_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
@@ -269,10 +270,10 @@ static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
+static int sst_byt_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
@@ -294,7 +295,8 @@ out:
return ret;
}
-static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream,
+static int sst_byt_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -303,22 +305,11 @@ static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream,
return snd_pcm_lib_default_mmap(substream, vma);
}
-static const struct snd_pcm_ops sst_byt_pcm_ops = {
- .open = sst_byt_pcm_open,
- .close = sst_byt_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = sst_byt_pcm_hw_params,
- .hw_free = sst_byt_pcm_hw_free,
- .trigger = sst_byt_pcm_trigger,
- .pointer = sst_byt_pcm_pointer,
- .mmap = sst_byt_pcm_mmap,
-};
-
-static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int sst_byt_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
size_t size;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct sst_pdata *pdata = dev_get_platdata(component->dev);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
@@ -380,8 +371,15 @@ static int sst_byt_pcm_probe(struct snd_soc_component *component)
static const struct snd_soc_component_driver byt_dai_component = {
.name = DRV_NAME,
.probe = sst_byt_pcm_probe,
- .ops = &sst_byt_pcm_ops,
- .pcm_new = sst_byt_pcm_new,
+ .open = sst_byt_pcm_open,
+ .close = sst_byt_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = sst_byt_pcm_hw_params,
+ .hw_free = sst_byt_pcm_hw_free,
+ .trigger = sst_byt_pcm_trigger,
+ .pointer = sst_byt_pcm_pointer,
+ .mmap = sst_byt_pcm_mmap,
+ .pcm_construct = sst_byt_pcm_new,
};
#ifdef CONFIG_PM
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index 5c27f7ab4a5f..6c9fd9ad566e 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -263,14 +263,17 @@ config SND_SOC_INTEL_DA7219_MAX98357A_GENERIC
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
+config SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON
+ tristate
+ select SND_SOC_INTEL_DA7219_MAX98357A_GENERIC
+
if SND_SOC_INTEL_APL
config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH
tristate "Broxton with DA7219 and MAX98357A in I2S Mode"
depends on I2C && ACPI
depends on MFD_INTEL_LPSS || COMPILE_TEST
- select SND_SOC_INTEL_DA7219_MAX98357A_GENERIC
- select SND_HDA_DSP_LOADER
+ select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON
help
This adds support for ASoC machine driver for Broxton-P platforms
with DA7219 + MAX98357A I2S audio codec.
@@ -284,7 +287,6 @@ config SND_SOC_INTEL_BXT_RT298_MACH
select SND_SOC_RT298
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
- select SND_HDA_DSP_LOADER
help
This adds support for ASoC machine driver for Broxton platforms
with RT286 I2S audio codec.
@@ -320,6 +322,7 @@ config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH
select SND_SOC_RT5514_SPI
select SND_SOC_MAX98927
select SND_SOC_HDAC_HDMI
+ select SND_SOC_INTEL_SKYLAKE_SSP_CLK
help
This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for RT5663 + RT5514 + MAX98927.
@@ -364,7 +367,18 @@ config SND_SOC_INTEL_KBL_RT5660_MACH
endif ## SND_SOC_INTEL_KBL
-if SND_SOC_INTEL_GLK || (SND_SOC_SOF_GEMINILAKE && SND_SOC_SOF_HDA_LINK)
+if SND_SOC_SOF_GEMINILAKE && SND_SOC_SOF_HDA_LINK
+
+config SND_SOC_INTEL_GLK_DA7219_MAX98357A_MACH
+ tristate "GLK with DA7219 and MAX98357A in I2S Mode"
+ depends on I2C && ACPI
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON
+ help
+ This adds support for ASoC machine driver for Geminilake platforms
+ with DA7219 + MAX98357A I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH
tristate "GLK with RT5682 and MAX98357A in I2S Mode"
@@ -374,14 +388,13 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH
select SND_SOC_MAX98357A
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
- select SND_HDA_DSP_LOADER
help
This adds support for ASoC machine driver for Geminilake platforms
with RT5682 + MAX98357A I2S audio codec.
Say Y if you have such a device.
If unsure select "N".
-endif ## SND_SOC_INTEL_GLK || (SND_SOC_SOF_GEMINILAKE && SND_SOC_SOF_HDA_LINK)
+endif ## SND_SOC_SOF_GEMINILAKE && SND_SOC_SOF_HDA_LINK
if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC
@@ -398,11 +411,11 @@ config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH
endif ## SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC
-if SND_SOC_SOF_HDA_COMMON || SND_SOC_SOF_BAYTRAIL
+if SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL
config SND_SOC_INTEL_SOF_RT5682_MACH
tristate "SOF with rt5682 codec in I2S Mode"
depends on I2C && ACPI
- depends on (SND_SOC_SOF_HDA_COMMON && (MFD_INTEL_LPSS || COMPILE_TEST)) ||\
+ depends on (SND_SOC_SOF_HDA_LINK && (MFD_INTEL_LPSS || COMPILE_TEST)) ||\
(SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST))
select SND_SOC_RT5682
select SND_SOC_DMIC
@@ -412,7 +425,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH
with rt5682 codec.
Say Y if you have such a device.
If unsure select "N".
-endif ## SND_SOC_SOF_HDA_COMMON || SND_SOC_SOF_BAYTRAIL
+endif ## SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL
if (SND_SOC_SOF_COMETLAKE_LP && SND_SOC_SOF_HDA_LINK)
@@ -420,7 +433,26 @@ config SND_SOC_INTEL_CML_LP_DA7219_MAX98357A_MACH
tristate "CML_LP with DA7219 and MAX98357A in I2S Mode"
depends on I2C && ACPI
depends on MFD_INTEL_LPSS || COMPILE_TEST
- select SND_SOC_INTEL_DA7219_MAX98357A_GENERIC
+ select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON
+ help
+ This adds support for ASoC machine driver for Cometlake platforms
+ with DA7219 + MAX98357A I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH
+ tristate "CML with RT1011 and RT5682 in I2S Mode"
+ depends on I2C && ACPI
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT1011
+ select SND_SOC_RT5682
+ select SND_SOC_DMIC
+ select SND_SOC_HDAC_HDMI
+ help
+ This adds support for ASoC machine driver for SOF platform with
+ RT1011 + RT5682 I2S codec.
+ Say Y if you have such a device.
+ If unsure select "N".
endif ## 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 6445f90ea542..ba1aa89db09d 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -4,9 +4,9 @@ snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
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
-snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
-snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.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-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
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
@@ -17,14 +17,15 @@ snd-soc-sst-byt-cht-cx2072x-objs := bytcht_cx2072x.o
snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o
snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o
snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o
-snd-soc-sof_rt5682-objs := sof_rt5682.o
+snd-soc-sof_rt5682-objs := sof_rt5682.o hda_dsp_common.o
+snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o hda_dsp_common.o
snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o
snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o
snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o
snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o
snd-soc-kbl_rt5660-objs := kbl_rt5660.o
snd-soc-skl_rt286-objs := skl_rt286.o
-snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o
+snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o hda_dsp_common.o
snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o
snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
@@ -32,7 +33,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
-obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.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_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
@@ -47,6 +48,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_CX2072X_MACH) += snd-soc-sst-byt-cht-cx2072x.
obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH) += snd-soc-cml_rt1011_rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH) += snd-soc-kbl_da7219_max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH) += snd-soc-kbl_da7219_max98927.o
obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH) += snd-soc-kbl_rt5663_max98927.o
diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c
index 4a4d3353e26d..2af8e5a62da8 100644
--- a/sound/soc/intel/boards/bdw-rt5677.c
+++ b/sound/soc/intel/boards/bdw-rt5677.c
@@ -74,6 +74,11 @@ static const struct snd_soc_dapm_route bdw_rt5677_map[] = {
/* CODEC BE connections */
{"SSP0 CODEC IN", NULL, "AIF1 Capture"},
{"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+ {"DSP Capture", NULL, "DSP Buffer"},
+
+ /* DSP Clock Connections */
+ { "DSP Buffer", NULL, "SSP0 CODEC IN" },
+ { "SSP0 CODEC IN", NULL, "DSPTX" },
};
static const struct snd_kcontrol_new bdw_rt5677_controls[] = {
@@ -165,10 +170,37 @@ static int bdw_rt5677_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+static int bdw_rt5677_dsp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_PLL1, 24576000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+ return ret;
+ }
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5677_PLL1_S_MCLK,
+ 24000000, 24576000);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll configuration\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_ops bdw_rt5677_ops = {
.hw_params = bdw_rt5677_hw_params,
};
+static const struct snd_soc_ops bdw_rt5677_dsp_ops = {
+ .hw_params = bdw_rt5677_dsp_hw_params,
+};
+
#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd)
{
@@ -208,6 +240,11 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd)
rt5677_sel_asrc_clk_src(component, RT5677_DA_STEREO_FILTER |
RT5677_AD_STEREO1_FILTER | RT5677_I2S1_SOURCE,
RT5677_CLK_SEL_I2S1_ASRC);
+ /* Enable codec ASRC function for Mono ADC L.
+ * The ASRC clock source is clk_sys2_asrc.
+ */
+ rt5677_sel_asrc_clk_src(component, RT5677_AD_MONO_L_FILTER,
+ RT5677_CLK_SEL_SYS2);
/* Request rt5677 GPIO for headphone amp control */
bdw_rt5677->gpio_hp_en = devm_gpiod_get(component->dev, "headphone-enable",
@@ -258,6 +295,12 @@ SND_SOC_DAILINK_DEF(platform,
SND_SOC_DAILINK_DEF(be,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RT5677CE:00", "rt5677-aif1")));
+/* Wake on voice interface */
+SND_SOC_DAILINK_DEFS(dsp,
+ DAILINK_COMP_ARRAY(COMP_CPU("spi-RT5677AA:00")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RT5677CE:00", "rt5677-dspbuffer")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("spi-RT5677AA:00")));
+
static struct snd_soc_dai_link bdw_rt5677_dais[] = {
/* Front End DAI links */
{
@@ -276,6 +319,14 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = {
SND_SOC_DAILINK_REG(fe, dummy, platform),
},
+ /* Non-DPCM links */
+ {
+ .name = "Codec DSP",
+ .stream_name = "Wake on Voice",
+ .ops = &bdw_rt5677_dsp_ops,
+ SND_SOC_DAILINK_REG(dsp),
+ },
+
/* Back End DAI links */
{
/* SSP0 - Codec */
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
index ac1dea5f9d11..5873abb46441 100644
--- a/sound/soc/intel/boards/bxt_da7219_max98357a.c
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -21,6 +21,7 @@
#include "../../codecs/da7219.h"
#include "../../codecs/da7219-aad.h"
#include "../common/soc-intel-quirks.h"
+#include "hda_dsp_common.h"
#define BXT_DIALOG_CODEC_DAI "da7219-hifi"
#define BXT_MAXIM_CODEC_DAI "HiFi"
@@ -38,6 +39,7 @@ struct bxt_hdmi_pcm {
struct bxt_card_private {
struct list_head hdmi_pcm_list;
+ bool common_hdmi_codec_drv;
};
enum {
@@ -615,6 +617,13 @@ static int bxt_card_late_probe(struct snd_soc_card *card)
snd_soc_dapm_add_routes(&card->dapm, broxton_map,
ARRAY_SIZE(broxton_map));
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct bxt_hdmi_pcm,
+ head);
+ component = pcm->codec_dai->component;
+
+ if (ctx->common_hdmi_codec_drv)
+ return hda_dsp_hdmi_build_controls(card, component);
+
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
component = pcm->codec_dai->component;
snprintf(jack_name, sizeof(jack_name),
@@ -720,6 +729,8 @@ static int broxton_audio_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
+
return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card);
}
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index adf416a49b48..eabf9d8468ae 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -18,6 +18,7 @@
#include <sound/pcm_params.h>
#include "../../codecs/hdac_hdmi.h"
#include "../../codecs/rt298.h"
+#include "hda_dsp_common.h"
/* Headset jack detection DAPM pins */
static struct snd_soc_jack broxton_headset;
@@ -31,6 +32,7 @@ struct bxt_hdmi_pcm {
struct bxt_rt286_private {
struct list_head hdmi_pcm_list;
+ bool common_hdmi_codec_drv;
};
enum {
@@ -527,6 +529,13 @@ static int bxt_card_late_probe(struct snd_soc_card *card)
int err, i = 0;
char jack_name[NAME_SIZE];
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct bxt_hdmi_pcm,
+ head);
+ component = pcm->codec_dai->component;
+
+ if (ctx->common_hdmi_codec_drv)
+ return hda_dsp_hdmi_build_controls(card, component);
+
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
component = pcm->codec_dai->component;
snprintf(jack_name, sizeof(jack_name),
@@ -626,6 +635,8 @@ static int broxton_audio_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
+
return devm_snd_soc_register_card(&pdev->dev, card);
}
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 8879c3be29d5..c68a5b85a4a0 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -48,6 +48,7 @@ struct cht_mc_private {
#define CHT_RT5645_SSP2_AIF2 BIT(16) /* default is using AIF1 */
#define CHT_RT5645_SSP0_AIF1 BIT(17)
#define CHT_RT5645_SSP0_AIF2 BIT(18)
+#define CHT_RT5645_PMC_PLT_CLK_0 BIT(19)
static unsigned long cht_rt5645_quirk = 0;
@@ -59,6 +60,8 @@ static void log_quirks(struct device *dev)
dev_info(dev, "quirk SSP0_AIF1 enabled");
if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)
dev_info(dev, "quirk SSP0_AIF2 enabled");
+ if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0)
+ dev_info(dev, "quirk PMC_PLT_CLK_0 enabled");
}
static int platform_clock_control(struct snd_soc_dapm_widget *w,
@@ -226,16 +229,22 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-/* uncomment when we have a real quirk
static int cht_rt5645_quirk_cb(const struct dmi_system_id *id)
{
cht_rt5645_quirk = (unsigned long)id->driver_data;
return 1;
}
-*/
static const struct dmi_system_id cht_rt5645_quirk_table[] = {
{
+ /* Strago family Chromebooks */
+ .callback = cht_rt5645_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_Strago"),
+ },
+ .driver_data = (void *)CHT_RT5645_PMC_PLT_CLK_0,
+ },
+ {
},
};
@@ -526,6 +535,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
int dai_index = 0;
int ret_val = 0;
int i;
+ const char *mclk_name;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
@@ -662,11 +672,15 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
if (ret_val)
return ret_val;
- drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+ if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0)
+ mclk_name = "pmc_plt_clk_0";
+ else
+ mclk_name = "pmc_plt_clk_3";
+
+ drv->mclk = devm_clk_get(&pdev->dev, mclk_name);
if (IS_ERR(drv->mclk)) {
- dev_err(&pdev->dev,
- "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
- PTR_ERR(drv->mclk));
+ dev_err(&pdev->dev, "Failed to get MCLK from %s: %ld\n",
+ mclk_name, PTR_ERR(drv->mclk));
return PTR_ERR(drv->mclk);
}
diff --git a/sound/soc/intel/boards/cml_rt1011_rt5682.c b/sound/soc/intel/boards/cml_rt1011_rt5682.c
new file mode 100644
index 000000000000..a22f97234201
--- /dev/null
+++ b/sound/soc/intel/boards/cml_rt1011_rt5682.c
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2019 Intel Corporation.
+
+/*
+ * Intel Cometlake I2S Machine driver for RT1011 + RT5682 codec
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/dmi.h>
+#include <linux/slab.h>
+#include <asm/cpu_device_id.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/rt5682.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/rt1011.h"
+#include "../../codecs/rt5682.h"
+#include "../../codecs/hdac_hdmi.h"
+#include "hda_dsp_common.h"
+
+/* The platform clock outputs 24Mhz clock to codec as I2S MCLK */
+#define CML_PLAT_CLK 24000000
+#define CML_RT1011_CODEC_DAI "rt1011-aif"
+#define CML_RT5682_CODEC_DAI "rt5682-aif1"
+#define NAME_SIZE 32
+
+static struct snd_soc_jack hdmi_jack[3];
+
+struct hdmi_pcm {
+ struct list_head head;
+ struct snd_soc_dai *codec_dai;
+ int device;
+};
+
+struct card_private {
+ char codec_name[SND_ACPI_I2C_ID_LEN];
+ struct snd_soc_jack headset;
+ struct list_head hdmi_pcm_list;
+ bool common_hdmi_codec_drv;
+};
+
+static const struct snd_kcontrol_new cml_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("TL Ext Spk"),
+ SOC_DAPM_PIN_SWITCH("TR Ext Spk"),
+ SOC_DAPM_PIN_SWITCH("WL Ext Spk"),
+ SOC_DAPM_PIN_SWITCH("WR Ext Spk"),
+};
+
+static const struct snd_soc_dapm_widget cml_rt1011_rt5682_widgets[] = {
+ SND_SOC_DAPM_SPK("TL Ext Spk", NULL),
+ SND_SOC_DAPM_SPK("TR Ext Spk", NULL),
+ SND_SOC_DAPM_SPK("WL Ext Spk", NULL),
+ SND_SOC_DAPM_SPK("WR Ext Spk", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route cml_rt1011_rt5682_map[] = {
+ /*speaker*/
+ {"TL Ext Spk", NULL, "TL SPO"},
+ {"TR Ext Spk", NULL, "TR SPO"},
+ {"WL Ext Spk", NULL, "WL SPO"},
+ {"WR Ext Spk", NULL, "WR SPO"},
+
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+
+ /* other jacks */
+ { "IN1P", NULL, "Headset Mic" },
+
+ /* DMIC */
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+static int cml_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = rtd->codec_dai->component;
+ struct snd_soc_jack *jack;
+ int ret;
+
+ /* need to enable ASRC function for 24MHz mclk rate */
+ rt5682_sel_asrc_clk_src(component, RT5682_DA_STEREO1_FILTER |
+ RT5682_AD_STEREO1_FILTER,
+ RT5682_CLK_SEL_I2S1_ASRC);
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ &ctx->headset, NULL, 0);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ jack = &ctx->headset;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret)
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+
+ return ret;
+};
+
+static int cml_rt5682_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int clk_id, clk_freq, pll_out, ret;
+
+ clk_id = RT5682_PLL1_S_MCLK;
+ clk_freq = CML_PLAT_CLK;
+
+ pll_out = params_rate(params) * 512;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
+ if (ret < 0)
+ dev_warn(rtd->dev, "snd_soc_dai_set_pll err = %d\n", ret);
+
+ /* Configure sysclk for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
+ pll_out, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_warn(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+
+ /*
+ * slot_width should be equal or large than data length, set them
+ * be the same
+ */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2,
+ params_width(params));
+ if (ret < 0)
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+}
+
+static int cml_rt1011_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;
+ struct snd_soc_card *card = rtd->card;
+ int srate, i, ret = 0;
+
+ srate = params_rate(params);
+
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+
+ /* 100 Fs to drive 24 bit data */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK,
+ 100 * srate, 256 * srate);
+ if (ret < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ RT1011_FS_SYS_PRE_S_PLL1,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+
+ /*
+ * Codec TDM is configured as 24 bit capture/ playback.
+ * 2 CH PB is done over 4 codecs - 2 Woofers and 2 Tweeters.
+ * The Left woofer and tweeter plays the Left playback data
+ * and similar by the Right.
+ * Hence 2 codecs (1 T and 1 W pair) share same Rx slot.
+ * The feedback is captured for each codec individually.
+ * Hence all 4 codecs use 1 Tx slot each for feedback.
+ */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1011:00")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ 0x4, 0x1, 4, 24);
+ if (ret < 0)
+ break;
+ }
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1011:02")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ 0x1, 0x1, 4, 24);
+ if (ret < 0)
+ break;
+ }
+ /* TDM Rx slot 2 is used for Right Woofer & Tweeters pair */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1011:01")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ 0x8, 0x2, 4, 24);
+ if (ret < 0)
+ break;
+ }
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1011:03")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ 0x2, 0x2, 4, 24);
+ if (ret < 0)
+ break;
+ }
+ }
+ if (ret < 0)
+ dev_err(rtd->dev,
+ "set codec TDM slot for %s failed with error %d\n",
+ codec_dai->component->name, ret);
+ return ret;
+}
+
+static struct snd_soc_ops cml_rt5682_ops = {
+ .hw_params = cml_rt5682_hw_params,
+};
+
+static const struct snd_soc_ops cml_rt1011_ops = {
+ .hw_params = cml_rt1011_hw_params,
+};
+
+static int sof_card_late_probe(struct snd_soc_card *card)
+{
+ struct card_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component = NULL;
+ char jack_name[NAME_SIZE];
+ struct hdmi_pcm *pcm;
+ int ret, i = 0;
+
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm,
+ head);
+ component = pcm->codec_dai->component;
+
+ if (ctx->common_hdmi_codec_drv)
+ return hda_dsp_hdmi_build_controls(card, component);
+
+ list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+ component = pcm->codec_dai->component;
+ snprintf(jack_name, sizeof(jack_name),
+ "HDMI/DP, pcm=%d Jack", pcm->device);
+ ret = snd_soc_card_jack_new(card, jack_name,
+ SND_JACK_AVOUT, &hdmi_jack[i],
+ NULL, 0);
+ if (ret)
+ return ret;
+
+ ret = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
+ &hdmi_jack[i]);
+ if (ret < 0)
+ return ret;
+
+ i++;
+ }
+ if (!component)
+ return -EINVAL;
+
+ return hdac_hdmi_jack_port_init(component, &card->dapm);
+}
+
+static int hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *dai = rtd->codec_dai;
+ struct hdmi_pcm *pcm;
+
+ pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+
+ pcm->device = dai->id;
+ pcm->codec_dai = dai;
+
+ list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+ return 0;
+}
+
+/* Cometlake digital audio interface glue - connects codec <--> CPU */
+
+SND_SOC_DAILINK_DEF(ssp0_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
+SND_SOC_DAILINK_DEF(ssp0_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00",
+ CML_RT5682_CODEC_DAI)));
+
+SND_SOC_DAILINK_DEF(ssp1_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("SSP1 Pin")));
+SND_SOC_DAILINK_DEF(ssp1_codec,
+ DAILINK_COMP_ARRAY(
+ /* WL */ COMP_CODEC("i2c-10EC1011:00", CML_RT1011_CODEC_DAI),
+ /* WR */ COMP_CODEC("i2c-10EC1011:01", CML_RT1011_CODEC_DAI),
+ /* TL */ COMP_CODEC("i2c-10EC1011:02", CML_RT1011_CODEC_DAI),
+ /* TR */ COMP_CODEC("i2c-10EC1011:03", CML_RT1011_CODEC_DAI)));
+
+SND_SOC_DAILINK_DEF(dmic_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin")));
+
+SND_SOC_DAILINK_DEF(dmic16k_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(idisp1_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin")));
+SND_SOC_DAILINK_DEF(idisp1_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1")));
+
+SND_SOC_DAILINK_DEF(idisp2_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin")));
+SND_SOC_DAILINK_DEF(idisp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2")));
+
+SND_SOC_DAILINK_DEF(idisp3_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin")));
+SND_SOC_DAILINK_DEF(idisp3_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3")));
+
+static struct snd_soc_dai_link cml_rt1011_rt5682_dailink[] = {
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "SSP0-Codec",
+ .id = 0,
+ .init = cml_rt5682_codec_init,
+ .ignore_pmdown_time = 1,
+ .ops = &cml_rt5682_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(ssp0_pin, ssp0_codec, platform),
+ },
+ {
+ .name = "dmic01",
+ .id = 1,
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform),
+ },
+ {
+ .name = "dmic16k",
+ .id = 2,
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(dmic16k_pin, dmic_codec, platform),
+ },
+ {
+ .name = "iDisp1",
+ .id = 3,
+ .init = hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform),
+ },
+ {
+ .name = "iDisp2",
+ .id = 4,
+ .init = hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform),
+ },
+ {
+ .name = "iDisp3",
+ .id = 5,
+ .init = hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform),
+ },
+ {
+ /*
+ * SSP1 - Codec : added to end of list ensuring
+ * reuse of common topologies for other end points
+ * and changing only SSP1's codec
+ */
+ .name = "SSP1-Codec",
+ .id = 6,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1, /* Capture stream provides Feedback */
+ .no_pcm = 1,
+ .ops = &cml_rt1011_ops,
+ SND_SOC_DAILINK_REG(ssp1_pin, ssp1_codec, platform),
+ },
+};
+
+static struct snd_soc_codec_conf rt1011_conf[] = {
+ {
+ .dev_name = "i2c-10EC1011:00",
+ .name_prefix = "WL",
+ },
+ {
+ .dev_name = "i2c-10EC1011:01",
+ .name_prefix = "WR",
+ },
+ {
+ .dev_name = "i2c-10EC1011:02",
+ .name_prefix = "TL",
+ },
+ {
+ .dev_name = "i2c-10EC1011:03",
+ .name_prefix = "TR",
+ },
+};
+
+/* Cometlake audio machine driver for RT1011 and RT5682 */
+static struct snd_soc_card snd_soc_card_cml = {
+ .name = "cml_rt1011_rt5682",
+ .dai_link = cml_rt1011_rt5682_dailink,
+ .num_links = ARRAY_SIZE(cml_rt1011_rt5682_dailink),
+ .codec_conf = rt1011_conf,
+ .num_configs = ARRAY_SIZE(rt1011_conf),
+ .dapm_widgets = cml_rt1011_rt5682_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cml_rt1011_rt5682_widgets),
+ .dapm_routes = cml_rt1011_rt5682_map,
+ .num_dapm_routes = ARRAY_SIZE(cml_rt1011_rt5682_map),
+ .controls = cml_controls,
+ .num_controls = ARRAY_SIZE(cml_controls),
+ .fully_routed = true,
+ .late_probe = sof_card_late_probe,
+};
+
+static int snd_cml_rt1011_probe(struct platform_device *pdev)
+{
+ struct card_private *ctx;
+ struct snd_soc_acpi_mach *mach;
+ const char *platform_name;
+ int ret;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
+ if (!ctx)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+ mach = (&pdev->dev)->platform_data;
+ snd_soc_card_cml.dev = &pdev->dev;
+ platform_name = mach->mach_params.platform;
+
+ /* set platform name for each dailink */
+ ret = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cml,
+ platform_name);
+ if (ret)
+ return ret;
+
+ ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
+
+ snd_soc_card_set_drvdata(&snd_soc_card_cml, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cml);
+}
+
+static struct platform_driver snd_cml_rt1011_rt5682_driver = {
+ .probe = snd_cml_rt1011_probe,
+ .driver = {
+ .name = "cml_rt1011_rt5682",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(snd_cml_rt1011_rt5682_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("Cometlake Audio Machine driver - RT1011 and RT5682 in I2S mode");
+MODULE_AUTHOR("Naveen Manohar <naveen.m@intel.com>");
+MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cml_rt1011_rt5682");
diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c
index bd2d371f2acd..b36264d1d1cd 100644
--- a/sound/soc/intel/boards/glk_rt5682_max98357a.c
+++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c
@@ -19,6 +19,7 @@
#include <sound/soc-acpi.h>
#include "../../codecs/rt5682.h"
#include "../../codecs/hdac_hdmi.h"
+#include "hda_dsp_common.h"
/* The platform clock outputs 19.2Mhz clock to codec as I2S MCLK */
#define GLK_PLAT_CLK_FREQ 19200000
@@ -41,6 +42,7 @@ struct glk_hdmi_pcm {
struct glk_card_private {
struct snd_soc_jack geminilake_headset;
struct list_head hdmi_pcm_list;
+ bool common_hdmi_codec_drv;
};
enum {
@@ -545,6 +547,13 @@ static int glk_card_late_probe(struct snd_soc_card *card)
int err = 0;
int i = 0;
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct glk_hdmi_pcm,
+ head);
+ component = pcm->codec_dai->component;
+
+ if (ctx->common_hdmi_codec_drv)
+ return hda_dsp_hdmi_build_controls(card, component);
+
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
component = pcm->codec_dai->component;
snprintf(jack_name, sizeof(jack_name),
@@ -612,6 +621,8 @@ static int geminilake_audio_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
+
return devm_snd_soc_register_card(&pdev->dev, card);
}
diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c
new file mode 100644
index 000000000000..ed36b68d6705
--- /dev/null
+++ b/sound/soc/intel/boards/hda_dsp_common.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright(c) 2019 Intel Corporation. All rights reserved.
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_i915.h>
+#include "../../codecs/hdac_hda.h"
+
+#include "hda_dsp_common.h"
+
+/*
+ * Search card topology and return PCM device number
+ * matching Nth HDMI device (zero-based index).
+ */
+struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card,
+ int hdmi_idx)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_pcm *spcm;
+ int i = 0;
+
+ for_each_card_rtds(card, rtd) {
+ spcm = rtd->pcm ?
+ rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].pcm : NULL;
+ if (spcm && strstr(spcm->id, "HDMI")) {
+ if (i == hdmi_idx)
+ return rtd->pcm;
+ ++i;
+ }
+ }
+
+ return NULL;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+/*
+ * Search card topology and register HDMI PCM related controls
+ * to codec driver.
+ */
+int hda_dsp_hdmi_build_controls(struct snd_soc_card *card,
+ struct snd_soc_component *comp)
+{
+ struct hdac_hda_priv *hda_pvt;
+ struct hda_codec *hcodec;
+ struct snd_pcm *spcm;
+ struct hda_pcm *hpcm;
+ int err = 0, i = 0;
+
+ if (!comp)
+ return -EINVAL;
+
+ hda_pvt = snd_soc_component_get_drvdata(comp);
+ hcodec = &hda_pvt->codec;
+
+ list_for_each_entry(hpcm, &hcodec->pcm_list_head, list) {
+ spcm = hda_dsp_hdmi_pcm_handle(card, i);
+ if (spcm) {
+ hpcm->pcm = spcm;
+ hpcm->device = spcm->device;
+ dev_dbg(card->dev,
+ "%s: mapping HDMI converter %d to PCM %d (%p)\n",
+ __func__, i, hpcm->device, spcm);
+ } else {
+ hpcm->pcm = 0;
+ hpcm->device = SNDRV_PCM_INVALID_DEVICE;
+ dev_warn(card->dev,
+ "%s: no PCM in topology for HDMI converter %d\n\n",
+ __func__, i);
+ }
+ i++;
+ }
+ snd_hdac_display_power(hcodec->core.bus,
+ HDA_CODEC_IDX_CONTROLLER, true);
+ err = snd_hda_codec_build_controls(hcodec);
+ if (err < 0)
+ dev_err(card->dev, "unable to create controls %d\n", err);
+ snd_hdac_display_power(hcodec->core.bus,
+ HDA_CODEC_IDX_CONTROLLER, false);
+
+ return err;
+}
+
+#endif
diff --git a/sound/soc/intel/boards/hda_dsp_common.h b/sound/soc/intel/boards/hda_dsp_common.h
new file mode 100644
index 000000000000..431f7f09dccb
--- /dev/null
+++ b/sound/soc/intel/boards/hda_dsp_common.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2019 Intel Corporation.
+ */
+
+/*
+ * This file defines helper functions used by multiple
+ * Intel HDA based machine drivers.
+ */
+
+#ifndef __HDA_DSP_COMMON_H
+#define __HDA_DSP_COMMON_H
+
+#include <sound/hda_codec.h>
+#include <sound/hda_i915.h>
+#include "../../codecs/hdac_hda.h"
+
+struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card,
+ int hdmi_idx);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+int hda_dsp_hdmi_build_controls(struct snd_soc_card *card,
+ struct snd_soc_component *comp);
+#else
+static inline int hda_dsp_hdmi_build_controls(struct snd_soc_card *card,
+ struct snd_soc_component *comp)
+{
+ return -EINVAL;
+}
+#endif
+
+#endif /* __HDA_DSP_COMMON_H */
diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
index 74dda8784f1a..3e5f6bead229 100644
--- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
@@ -22,6 +22,9 @@
#include "../../codecs/rt5514.h"
#include "../../codecs/rt5663.h"
#include "../../codecs/hdac_hdmi.h"
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#define KBL_REALTEK_CODEC_DAI "rt5663-aif"
#define KBL_REALTEK_DMIC_CODEC_DAI "rt5514-aif1"
@@ -50,6 +53,8 @@ struct kbl_codec_private {
struct snd_soc_jack kabylake_headset;
struct list_head hdmi_pcm_list;
struct snd_soc_jack kabylake_hdmi[2];
+ struct clk *mclk;
+ struct clk *sclk;
};
enum {
@@ -71,6 +76,61 @@ static const struct snd_kcontrol_new kabylake_controls[] = {
SOC_DAPM_PIN_SWITCH("DMIC"),
};
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct kbl_codec_private *priv = snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ /*
+ * MCLK/SCLK need to be ON early for a successful synchronization of
+ * codec internal clock. And the clocks are turned off during
+ * POST_PMD after the stream is stopped.
+ */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable MCLK */
+ ret = clk_set_rate(priv->mclk, 24000000);
+ if (ret < 0) {
+ dev_err(card->dev, "Can't set rate for mclk, err: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(card->dev, "Can't enable mclk, err: %d\n", ret);
+ return ret;
+ }
+
+ /* Enable SCLK */
+ ret = clk_set_rate(priv->sclk, 3072000);
+ if (ret < 0) {
+ dev_err(card->dev, "Can't set rate for sclk, err: %d\n",
+ ret);
+ clk_disable_unprepare(priv->mclk);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->sclk);
+ if (ret < 0) {
+ dev_err(card->dev, "Can't enable sclk, err: %d\n", ret);
+ clk_disable_unprepare(priv->mclk);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(priv->mclk);
+ clk_disable_unprepare(priv->sclk);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget kabylake_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -79,11 +139,15 @@ static const struct snd_soc_dapm_widget kabylake_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
SND_SOC_DAPM_SPK("HDMI1", NULL),
SND_SOC_DAPM_SPK("HDMI2", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route kabylake_map[] = {
/* Headphones */
+ { "Headphone Jack", NULL, "Platform Clock" },
{ "Headphone Jack", NULL, "HPOL" },
{ "Headphone Jack", NULL, "HPOR" },
@@ -92,6 +156,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = {
{ "Right Spk", NULL, "Right BE_OUT" },
/* other jacks */
+ { "Headset Mic", NULL, "Platform Clock" },
{ "IN1P", NULL, "Headset Mic" },
{ "IN1N", NULL, "Headset Mic" },
@@ -400,6 +465,9 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
dmic_constraints);
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
}
@@ -588,6 +656,55 @@ static struct snd_soc_dai_link kabylake_dais[] = {
},
};
+static int kabylake_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+ struct snd_soc_component *component = dapm->component;
+ struct kbl_codec_private *priv = snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ if (!component || strcmp(component->name, RT5514_DEV_NAME))
+ return 0;
+
+ if (IS_ERR(priv->mclk))
+ return 0;
+
+ /*
+ * It's required to control mclk directly in the set_bias_level
+ * function for rt5514 codec or the recording function could
+ * break.
+ */
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_ON) {
+ dev_dbg(card->dev, "Disable mclk");
+ clk_disable_unprepare(priv->mclk);
+ } else {
+ dev_dbg(card->dev, "Enable mclk");
+ ret = clk_set_rate(priv->mclk, 24000000);
+ if (ret) {
+ dev_err(card->dev, "Can't set rate for mclk, err: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(card->dev, "Can't enable mclk, err: %d\n",
+ ret);
+
+ /* mclk is already enabled in FW */
+ ret = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
static int kabylake_card_late_probe(struct snd_soc_card *card)
{
struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(card);
@@ -623,10 +740,11 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
* kabylake audio machine driver for MAX98927 + RT5514 + RT5663
*/
static struct snd_soc_card kabylake_audio_card = {
- .name = "kbl_r5514_5663_max",
+ .name = "kbl-r5514-5663-max",
.owner = THIS_MODULE,
.dai_link = kabylake_dais,
.num_links = ARRAY_SIZE(kabylake_dais),
+ .set_bias_level = kabylake_set_bias_level,
.controls = kabylake_controls,
.num_controls = ARRAY_SIZE(kabylake_controls),
.dapm_widgets = kabylake_widgets,
@@ -643,6 +761,7 @@ static int kabylake_audio_probe(struct platform_device *pdev)
{
struct kbl_codec_private *ctx;
struct snd_soc_acpi_mach *mach;
+ int ret = 0;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -658,6 +777,34 @@ static int kabylake_audio_probe(struct platform_device *pdev)
dmic_constraints = mach->mach_params.dmic_num == 2 ?
&constraints_dmic_2ch : &constraints_dmic_channels;
+ ctx->mclk = devm_clk_get(&pdev->dev, "ssp1_mclk");
+ if (IS_ERR(ctx->mclk)) {
+ ret = PTR_ERR(ctx->mclk);
+ if (ret == -ENOENT) {
+ dev_info(&pdev->dev,
+ "Failed to get ssp1_mclk, defer probe\n");
+ return -EPROBE_DEFER;
+ }
+
+ dev_err(&pdev->dev, "Failed to get ssp1_mclk with err:%d\n",
+ ret);
+ return ret;
+ }
+
+ ctx->sclk = devm_clk_get(&pdev->dev, "ssp1_sclk");
+ if (IS_ERR(ctx->sclk)) {
+ ret = PTR_ERR(ctx->sclk);
+ if (ret == -ENOENT) {
+ dev_info(&pdev->dev,
+ "Failed to get ssp1_sclk, defer probe\n");
+ return -EPROBE_DEFER;
+ }
+
+ dev_err(&pdev->dev, "Failed to get ssp1_sclk with err:%d\n",
+ ret);
+ return ret;
+ }
+
return devm_snd_soc_register_card(&pdev->dev, &kabylake_audio_card);
}
diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c
index 58409b6e476e..eb419e1ec42b 100644
--- a/sound/soc/intel/boards/skl_hda_dsp_common.c
+++ b/sound/soc/intel/boards/skl_hda_dsp_common.c
@@ -14,6 +14,9 @@
#include "../../codecs/hdac_hdmi.h"
#include "skl_hda_dsp_common.h"
+#include <sound/hda_codec.h>
+#include "../../codecs/hdac_hda.h"
+
#define NAME_SIZE 32
int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device)
@@ -136,6 +139,9 @@ int skl_hda_hdmi_jack_init(struct snd_soc_card *card)
char jack_name[NAME_SIZE];
int err;
+ if (ctx->common_hdmi_codec_drv)
+ return skl_hda_hdmi_build_controls(card);
+
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
component = pcm->codec_dai->component;
snprintf(jack_name, sizeof(jack_name),
diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h
index daa582e513b2..d6150670ca05 100644
--- a/sound/soc/intel/boards/skl_hda_dsp_common.h
+++ b/sound/soc/intel/boards/skl_hda_dsp_common.h
@@ -8,12 +8,15 @@
* platforms with HDA Codecs.
*/
-#ifndef __SOUND_SOC_HDA_DSP_COMMON_H
-#define __SOUND_SOC_HDA_DSP_COMMON_H
+#ifndef __SKL_HDA_DSP_COMMON_H
+#define __SKL_HDA_DSP_COMMON_H
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/jack.h>
+#include <sound/hda_codec.h>
+#include "../../codecs/hdac_hda.h"
+#include "hda_dsp_common.h"
#define HDA_DSP_MAX_BE_DAI_LINKS 7
@@ -29,10 +32,30 @@ struct skl_hda_private {
int pcm_count;
int dai_index;
const char *platform_name;
+ bool common_hdmi_codec_drv;
};
extern struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS];
int skl_hda_hdmi_jack_init(struct snd_soc_card *card);
int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device);
+/*
+ * Search card topology and register HDMI PCM related controls
+ * to codec driver.
+ */
+static inline int skl_hda_hdmi_build_controls(struct snd_soc_card *card)
+{
+ struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+ struct skl_hda_hdmi_pcm *pcm;
+
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct skl_hda_hdmi_pcm,
+ head);
+ component = pcm->codec_dai->component;
+ if (!component)
+ return -EINVAL;
+
+ return hda_dsp_hdmi_build_controls(card, component);
+}
+
#endif /* __SOUND_SOC_HDA_DSP_COMMON_H */
diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c
index 1778acdc367c..4e45901e3a2f 100644
--- a/sound/soc/intel/boards/skl_hda_dsp_generic.c
+++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c
@@ -90,7 +90,7 @@ skl_hda_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link)
}
static struct snd_soc_card hda_soc_card = {
- .name = "skl_hda_card",
+ .name = "hda-dsp",
.owner = THIS_MODULE,
.dai_link = skl_hda_be_dai_links,
.dapm_widgets = skl_hda_widgets,
@@ -178,6 +178,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev)
ctx->pcm_count = hda_soc_card.num_links;
ctx->dai_index = 1; /* hdmi codec dai name starts from index 1 */
ctx->platform_name = mach->mach_params.platform;
+ ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
hda_soc_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&hda_soc_card, ctx);
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 4f6e58c3954a..751b8ea6ae1f 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -21,6 +21,7 @@
#include "../../codecs/rt5682.h"
#include "../../codecs/hdac_hdmi.h"
#include "../common/soc-intel-quirks.h"
+#include "hda_dsp_common.h"
#define NAME_SIZE 32
@@ -53,6 +54,7 @@ struct sof_card_private {
struct clk *mclk;
struct snd_soc_jack sof_headset;
struct list_head hdmi_pcm_list;
+ bool common_hdmi_codec_drv;
};
static int sof_rt5682_quirk_cb(const struct dmi_system_id *id)
@@ -274,6 +276,13 @@ static int sof_card_late_probe(struct snd_soc_card *card)
if (is_legacy_cpu)
return 0;
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm,
+ head);
+ component = pcm->codec_dai->component;
+
+ if (ctx->common_hdmi_codec_drv)
+ return hda_dsp_hdmi_build_controls(card, component);
+
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
component = pcm->codec_dai->component;
snprintf(jack_name, sizeof(jack_name),
@@ -370,7 +379,7 @@ static int dmic_init(struct snd_soc_pcm_runtime *rtd)
/* sof audio machine driver for rt5682 codec */
static struct snd_soc_card sof_audio_card_rt5682 = {
- .name = "sof_rt5682",
+ .name = "rt5682", /* the sof- prefix is added by the core */
.owner = THIS_MODULE,
.controls = sof_controls,
.num_controls = ARRAY_SIZE(sof_controls),
@@ -651,6 +660,8 @@ static int sof_audio_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
+
snd_soc_card_set_drvdata(&sof_audio_card_rt5682, ctx);
return devm_snd_soc_register_card(&pdev->dev,
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
index 18d9630ae9a2..bd352878f89a 100644
--- a/sound/soc/intel/common/Makefile
+++ b/sound/soc/intel/common/Makefile
@@ -7,8 +7,10 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m
soc-acpi-intel-hsw-bdw-match.o \
soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \
soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \
- soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o \
+ soc-acpi-intel-cnl-match.o soc-acpi-intel-cfl-match.o \
+ soc-acpi-intel-cml-match.o soc-acpi-intel-icl-match.o \
soc-acpi-intel-tgl-match.o soc-acpi-intel-ehl-match.o \
+ soc-acpi-intel-jsl-match.o \
soc-acpi-intel-hda-match.o
obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
diff --git a/sound/soc/intel/common/soc-acpi-intel-cfl-match.c b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c
new file mode 100644
index 000000000000..d6fd2026d0b8
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * soc-apci-intel-cfl-match.c - tables and support for CFL ACPI enumeration.
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_machines[] = {
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_machines);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Common ACPI Match module");
diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c
new file mode 100644
index 000000000000..5d08ae066738
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * soc-acpi-intel-cml-match.c - tables and support for CML ACPI enumeration.
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static struct snd_soc_acpi_codecs cml_codecs = {
+ .num_codecs = 1,
+ .codecs = {"10EC5682"}
+};
+
+static struct snd_soc_acpi_codecs cml_spk_codecs = {
+ .num_codecs = 1,
+ .codecs = {"MX98357A"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = {
+ {
+ .id = "DLGS7219",
+ .drv_name = "cml_da7219_max98357a",
+ .quirk_data = &cml_spk_codecs,
+ .sof_fw_filename = "sof-cml.ri",
+ .sof_tplg_filename = "sof-cml-da7219-max98357a.tplg",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "sof_rt5682",
+ .quirk_data = &cml_codecs,
+ .sof_fw_filename = "sof-cml.ri",
+ .sof_tplg_filename = "sof-cml-rt5682-max98357a.tplg",
+ },
+ {
+ .id = "10EC1011",
+ .drv_name = "cml_rt1011_rt5682",
+ .quirk_data = &cml_codecs,
+ .sof_fw_filename = "sof-cml.ri",
+ .sof_tplg_filename = "sof-cml-rt1011-rt5682.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "sof_rt5682",
+ .sof_fw_filename = "sof-cml.ri",
+ .sof_tplg_filename = "sof-cml-rt5682.tplg",
+ },
+
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Common ACPI Match module");
diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c
index 985aa366c9e8..27588841c8b0 100644
--- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c
@@ -14,16 +14,6 @@ static struct skl_machine_pdata cnl_pdata = {
.use_tplg_pcm = true,
};
-static struct snd_soc_acpi_codecs cml_codecs = {
- .num_codecs = 1,
- .codecs = {"10EC5682"}
-};
-
-static struct snd_soc_acpi_codecs cml_spk_codecs = {
- .num_codecs = 1,
- .codecs = {"MX98357A"}
-};
-
struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = {
{
.id = "INT34C2",
@@ -33,27 +23,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = {
.sof_fw_filename = "sof-cnl.ri",
.sof_tplg_filename = "sof-cnl-rt274.tplg",
},
- {
- .id = "DLGS7219",
- .drv_name = "cml_da7219_max98357a",
- .quirk_data = &cml_spk_codecs,
- .sof_fw_filename = "sof-cnl.ri",
- .sof_tplg_filename = "sof-cml-da7219-max98357a.tplg",
- },
- {
- .id = "MX98357A",
- .drv_name = "sof_rt5682",
- .quirk_data = &cml_codecs,
- .sof_fw_filename = "sof-cnl.ri",
- .sof_tplg_filename = "sof-cml-rt5682-max98357a.tplg",
- },
- {
- .id = "10EC5682",
- .drv_name = "sof_rt5682",
- .sof_fw_filename = "sof-cnl.ri",
- .sof_tplg_filename = "sof-cml-rt5682.tplg",
- },
-
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
new file mode 100644
index 000000000000..1c68a04f0c6e
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * soc-apci-intel-jsl-match.c - tables and support for JSL ACPI enumeration.
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_jsl_machines);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Common ACPI Match module");
diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c
index 7f4f6b755760..6e498a581d20 100644
--- a/sound/soc/intel/haswell/sst-haswell-pcm.c
+++ b/sound/soc/intel/haswell/sst-haswell-pcm.c
@@ -458,12 +458,12 @@ static int create_adsp_page_table(struct snd_pcm_substream *substream,
}
/* this may get called several times by oss emulation */
-static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int hsw_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct hsw_pcm_data *pcm_data;
struct sst_hsw *hsw = pdata->hsw;
@@ -656,16 +656,17 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int hsw_pcm_hw_free(struct snd_pcm_substream *substream)
+static int hsw_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_pcm_lib_free_pages(substream);
return 0;
}
-static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int hsw_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct hsw_pcm_data *pcm_data;
struct sst_hsw_stream *sst_stream;
@@ -770,11 +771,11 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
return pos;
}
-static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_soc_component *component,
+ 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 = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct hsw_pcm_data *pcm_data;
struct sst_hsw *hsw = pdata->hsw;
@@ -795,10 +796,21 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
return offset;
}
-static int hsw_pcm_open(struct snd_pcm_substream *substream)
+#ifdef CONFIG_SND_DMA_SGBUF
+static struct page *hsw_pcm_page(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return snd_pcm_sgbuf_ops_page(substream, offset);
+}
+#else
+#define hsw_pcm_page NULL
+#endif /* CONFIG_SND_DMA_SGBUF */
+
+static int hsw_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct hsw_pcm_data *pcm_data;
struct sst_hsw *hsw = pdata->hsw;
@@ -828,10 +840,10 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int hsw_pcm_close(struct snd_pcm_substream *substream)
+static int hsw_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component);
struct hsw_pcm_data *pcm_data;
struct sst_hsw *hsw = pdata->hsw;
@@ -862,17 +874,6 @@ out:
return ret;
}
-static const struct snd_pcm_ops hsw_pcm_ops = {
- .open = hsw_pcm_open,
- .close = hsw_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = hsw_pcm_hw_params,
- .hw_free = hsw_pcm_hw_free,
- .trigger = hsw_pcm_trigger,
- .pointer = hsw_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
-};
-
static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
{
struct sst_hsw *hsw = pdata->hsw;
@@ -930,10 +931,10 @@ static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
}
}
-static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int hsw_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct sst_pdata *pdata = dev_get_platdata(component->dev);
struct hsw_priv_data *priv_data = dev_get_drvdata(component->dev);
struct device *dev = pdata->dma_dev;
@@ -1121,8 +1122,15 @@ static const struct snd_soc_component_driver hsw_dai_component = {
.name = DRV_NAME,
.probe = hsw_pcm_probe,
.remove = hsw_pcm_remove,
- .ops = &hsw_pcm_ops,
- .pcm_new = hsw_pcm_new,
+ .open = hsw_pcm_open,
+ .close = hsw_pcm_close,
+ .hw_params = hsw_pcm_hw_params,
+ .hw_free = hsw_pcm_hw_free,
+ .trigger = hsw_pcm_trigger,
+ .pointer = hsw_pcm_pointer,
+ .page = hsw_pcm_page,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .pcm_construct = hsw_pcm_new,
.controls = hsw_volume_controls,
.num_controls = ARRAY_SIZE(hsw_volume_controls),
.dapm_widgets = widgets,
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 7f287424af9b..0850141c7d47 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -1081,7 +1081,8 @@ int skl_dai_load(struct snd_soc_component *cmp, int index,
return 0;
}
-static int skl_platform_open(struct snd_pcm_substream *substream)
+static int skl_platform_soc_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai_link *dai_link = rtd->dai_link;
@@ -1167,8 +1168,9 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream,
return 0;
}
-static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
+static int skl_platform_soc_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ int cmd)
{
struct hdac_bus *bus = get_bus_ctx(substream);
@@ -1178,8 +1180,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
return 0;
}
-static snd_pcm_uframes_t skl_platform_pcm_pointer
- (struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t skl_platform_soc_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
struct hdac_bus *bus = get_bus_ctx(substream);
@@ -1225,6 +1228,24 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer
return bytes_to_frames(substream->runtime, pos);
}
+static int skl_platform_soc_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
+{
+ return snd_pcm_lib_default_mmap(substream, area);
+}
+
+#ifdef CONFIG_SND_DMA_SGBUF
+static struct page *skl_platform_soc_page(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return snd_pcm_sgbuf_ops_page(substream, offset);
+}
+#else
+#define skl_platform_soc_page NULL
+#endif /* CONFIG_SND_DMA_SGBUF */
+
static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
u64 nsec)
{
@@ -1245,7 +1266,9 @@ static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
}
-static int skl_get_time_info(struct snd_pcm_substream *substream,
+static int skl_platform_soc_get_time_info(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
@@ -1277,24 +1300,16 @@ static int skl_get_time_info(struct snd_pcm_substream *substream,
return 0;
}
-static const struct snd_pcm_ops skl_platform_ops = {
- .open = skl_platform_open,
- .ioctl = snd_pcm_lib_ioctl,
- .trigger = skl_platform_pcm_trigger,
- .pointer = skl_platform_pcm_pointer,
- .get_time_info = skl_get_time_info,
- .mmap = snd_pcm_lib_default_mmap,
- .page = snd_pcm_sgbuf_ops_page,
-};
-
-static void skl_pcm_free(struct snd_pcm *pcm)
+static void skl_platform_soc_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
-static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int skl_platform_soc_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->cpu_dai;
struct hdac_bus *bus = dev_get_drvdata(dai->dev);
@@ -1458,7 +1473,7 @@ static int skl_platform_soc_probe(struct snd_soc_component *component)
return 0;
}
-static void skl_pcm_remove(struct snd_soc_component *component)
+static void skl_platform_soc_remove(struct snd_soc_component *component)
{
struct hdac_bus *bus = dev_get_drvdata(component->dev);
struct skl_dev *skl = bus_to_skl(bus);
@@ -1471,10 +1486,16 @@ static void skl_pcm_remove(struct snd_soc_component *component)
static const struct snd_soc_component_driver skl_component = {
.name = "pcm",
.probe = skl_platform_soc_probe,
- .remove = skl_pcm_remove,
- .ops = &skl_platform_ops,
- .pcm_new = skl_pcm_new,
- .pcm_free = skl_pcm_free,
+ .remove = skl_platform_soc_remove,
+ .open = skl_platform_soc_open,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .trigger = skl_platform_soc_trigger,
+ .pointer = skl_platform_soc_pointer,
+ .get_time_info = skl_platform_soc_get_time_info,
+ .mmap = skl_platform_soc_mmap,
+ .page = skl_platform_soc_page,
+ .pcm_construct = skl_platform_soc_new,
+ .pcm_destruct = skl_platform_soc_free,
.module_get_upon_open = 1, /* increment refcount when a pcm is opened */
};
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 13408de34055..38d48d101783 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -497,15 +497,13 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev)
struct jz4740_i2s *i2s;
struct resource *mem;
int ret;
- const struct of_device_id *match;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
- match = of_match_device(jz4740_of_matches, &pdev->dev);
- if (match)
- i2s->version = (enum jz47xx_i2s_version)match->data;
+ i2s->version =
+ (enum jz47xx_i2s_version)of_device_get_match_data(&pdev->dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2s->base = devm_ioremap_resource(&pdev->dev, mem);
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
index 6f69f314f2c2..e28fb3449f1d 100644
--- a/sound/soc/kirkwood/kirkwood-dma.c
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -98,7 +98,8 @@ kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
}
}
-static int kirkwood_dma_open(struct snd_pcm_substream *substream)
+static int kirkwood_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
int err;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -132,7 +133,7 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
"kirkwood-i2s", priv);
if (err)
- return -EBUSY;
+ return err;
/*
* Enable Error interrupts. We're only ack'ing them but
@@ -160,7 +161,8 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
return 0;
}
-static int kirkwood_dma_close(struct snd_pcm_substream *substream)
+static int kirkwood_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct kirkwood_dma_data *priv = kirkwood_priv(substream);
@@ -180,8 +182,9 @@ static int kirkwood_dma_close(struct snd_pcm_substream *substream)
return 0;
}
-static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int kirkwood_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -191,13 +194,15 @@ static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
+static int kirkwood_dma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
-static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
+static int kirkwood_dma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct kirkwood_dma_data *priv = kirkwood_priv(substream);
@@ -222,8 +227,9 @@ static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
- *substream)
+static snd_pcm_uframes_t kirkwood_dma_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct kirkwood_dma_data *priv = kirkwood_priv(substream);
snd_pcm_uframes_t count;
@@ -238,16 +244,6 @@ static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
return count;
}
-static const struct snd_pcm_ops kirkwood_dma_ops = {
- .open = kirkwood_dma_open,
- .close = kirkwood_dma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = kirkwood_dma_hw_params,
- .hw_free = kirkwood_dma_hw_free,
- .prepare = kirkwood_dma_prepare,
- .pointer = kirkwood_dma_pointer,
-};
-
static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
int stream)
{
@@ -267,7 +263,8 @@ static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
return 0;
}
-static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
+static int kirkwood_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -294,7 +291,8 @@ static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
+static void kirkwood_dma_free_dma_buffers(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
@@ -316,7 +314,13 @@ static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
const struct snd_soc_component_driver kirkwood_soc_component = {
.name = DRV_NAME,
- .ops = &kirkwood_dma_ops,
- .pcm_new = kirkwood_dma_new,
- .pcm_free = kirkwood_dma_free_dma_buffers,
+ .open = kirkwood_dma_open,
+ .close = kirkwood_dma_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = kirkwood_dma_hw_params,
+ .hw_free = kirkwood_dma_hw_free,
+ .prepare = kirkwood_dma_prepare,
+ .pointer = kirkwood_dma_pointer,
+ .pcm_construct = kirkwood_dma_new,
+ .pcm_destruct = kirkwood_dma_free_dma_buffers,
};
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 111e44b64b38..a656d2014127 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -125,6 +125,7 @@ config SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A
select SND_SOC_MAX98357A
select SND_SOC_BT_SCO
select SND_SOC_TS3A227E
+ select SND_SOC_CROS_EC_CODEC if CROS_EC
help
This adds ASoC driver for Mediatek MT8183 boards
with the MT6358 TS3A227E MAX98357A audio codec.
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
index 3ce527ce30ce..b6624d8d084b 100644
--- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -77,11 +77,10 @@ int mtk_afe_add_sub_dai_control(struct snd_soc_component *component)
}
EXPORT_SYMBOL_GPL(mtk_afe_add_sub_dai_control);
-static snd_pcm_uframes_t mtk_afe_pcm_pointer
- (struct snd_pcm_substream *substream)
+snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
const struct mtk_base_memif_data *memif_data = memif->data;
@@ -111,18 +110,13 @@ static snd_pcm_uframes_t mtk_afe_pcm_pointer
POINTER_RETURN_FRAMES:
return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
}
+EXPORT_SYMBOL_GPL(mtk_afe_pcm_pointer);
-const struct snd_pcm_ops mtk_afe_pcm_ops = {
- .ioctl = snd_pcm_lib_ioctl,
- .pointer = mtk_afe_pcm_pointer,
-};
-EXPORT_SYMBOL_GPL(mtk_afe_pcm_ops);
-
-int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+int mtk_afe_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
size_t size;
struct snd_pcm *pcm = rtd->pcm;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
size = afe->mtk_afe_hardware->buffer_bytes_max;
@@ -132,17 +126,19 @@ int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
}
EXPORT_SYMBOL_GPL(mtk_afe_pcm_new);
-void mtk_afe_pcm_free(struct snd_pcm *pcm)
+void mtk_afe_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
EXPORT_SYMBOL_GPL(mtk_afe_pcm_free);
const struct snd_soc_component_driver mtk_afe_pcm_platform = {
- .name = AFE_PCM_NAME,
- .ops = &mtk_afe_pcm_ops,
- .pcm_new = mtk_afe_pcm_new,
- .pcm_free = mtk_afe_pcm_free,
+ .name = AFE_PCM_NAME,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .pointer = mtk_afe_pcm_pointer,
+ .pcm_construct = mtk_afe_pcm_new,
+ .pcm_destruct = mtk_afe_pcm_free,
};
EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform);
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
index 88df6797732f..e550d11568c3 100644
--- a/sound/soc/mediatek/common/mtk-afe-platform-driver.h
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
@@ -10,7 +10,6 @@
#define _MTK_AFE_PLATFORM_DRIVER_H_
#define AFE_PCM_NAME "mtk-afe-pcm"
-extern const struct snd_pcm_ops mtk_afe_pcm_ops;
extern const struct snd_soc_component_driver mtk_afe_pcm_platform;
struct mtk_base_afe;
@@ -18,9 +17,12 @@ struct snd_pcm;
struct snd_soc_component;
struct snd_soc_pcm_runtime;
-
-int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd);
-void mtk_afe_pcm_free(struct snd_pcm *pcm);
+snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+int mtk_afe_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd);
+void mtk_afe_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm);
int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe);
int mtk_afe_add_sub_dai_control(struct snd_soc_component *component);
diff --git a/sound/soc/mediatek/common/mtk-btcvsd.c b/sound/soc/mediatek/common/mtk-btcvsd.c
index d00608c73c6e..2b490ae2e642 100644
--- a/sound/soc/mediatek/common/mtk-btcvsd.c
+++ b/sound/soc/mediatek/common/mtk-btcvsd.c
@@ -875,11 +875,9 @@ static const struct snd_pcm_hardware mtk_btcvsd_hardware = {
.fifo_size = 0,
};
-static int mtk_pcm_btcvsd_open(struct snd_pcm_substream *substream)
+static int mtk_pcm_btcvsd_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME);
struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
int ret;
@@ -899,11 +897,9 @@ static int mtk_pcm_btcvsd_open(struct snd_pcm_substream *substream)
return ret;
}
-static int mtk_pcm_btcvsd_close(struct snd_pcm_substream *substream)
+static int mtk_pcm_btcvsd_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME);
struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream);
@@ -914,12 +910,10 @@ static int mtk_pcm_btcvsd_close(struct snd_pcm_substream *substream)
return 0;
}
-static int mtk_pcm_btcvsd_hw_params(struct snd_pcm_substream *substream,
+static int mtk_pcm_btcvsd_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME);
struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
@@ -934,11 +928,9 @@ static int mtk_pcm_btcvsd_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int mtk_pcm_btcvsd_hw_free(struct snd_pcm_substream *substream)
+static int mtk_pcm_btcvsd_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME);
struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -947,11 +939,9 @@ static int mtk_pcm_btcvsd_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int mtk_pcm_btcvsd_prepare(struct snd_pcm_substream *substream)
+static int mtk_pcm_btcvsd_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME);
struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream);
@@ -961,11 +951,9 @@ static int mtk_pcm_btcvsd_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int mtk_pcm_btcvsd_trigger(struct snd_pcm_substream *substream, int cmd)
+static int mtk_pcm_btcvsd_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME);
struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream);
int stream = substream->stream;
@@ -993,12 +981,10 @@ static int mtk_pcm_btcvsd_trigger(struct snd_pcm_substream *substream, int cmd)
}
}
-static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer
- (struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME);
struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
struct mtk_btcvsd_snd_stream *bt_stream;
snd_pcm_uframes_t frame = 0;
@@ -1044,13 +1030,11 @@ static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer
return frame;
}
-static int mtk_pcm_btcvsd_copy(struct snd_pcm_substream *substream,
+static int mtk_pcm_btcvsd_copy(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
int channel, unsigned long pos,
void __user *buf, unsigned long count)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME);
struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -1061,18 +1045,6 @@ static int mtk_pcm_btcvsd_copy(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_pcm_ops mtk_btcvsd_ops = {
- .open = mtk_pcm_btcvsd_open,
- .close = mtk_pcm_btcvsd_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = mtk_pcm_btcvsd_hw_params,
- .hw_free = mtk_pcm_btcvsd_hw_free,
- .prepare = mtk_pcm_btcvsd_prepare,
- .trigger = mtk_pcm_btcvsd_trigger,
- .pointer = mtk_pcm_btcvsd_pointer,
- .copy_user = mtk_pcm_btcvsd_copy,
-};
-
/* kcontrol */
static const char *const btsco_band_str[] = {"NB", "WB"};
@@ -1295,9 +1267,17 @@ static int mtk_btcvsd_snd_component_probe(struct snd_soc_component *component)
}
static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = {
- .name = BTCVSD_SND_NAME,
- .ops = &mtk_btcvsd_ops,
- .probe = mtk_btcvsd_snd_component_probe,
+ .name = BTCVSD_SND_NAME,
+ .probe = mtk_btcvsd_snd_component_probe,
+ .open = mtk_pcm_btcvsd_open,
+ .close = mtk_pcm_btcvsd_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = mtk_pcm_btcvsd_hw_params,
+ .hw_free = mtk_pcm_btcvsd_hw_free,
+ .prepare = mtk_pcm_btcvsd_prepare,
+ .trigger = mtk_pcm_btcvsd_trigger,
+ .pointer = mtk_pcm_btcvsd_pointer,
+ .copy_user = mtk_pcm_btcvsd_copy,
};
static int mtk_btcvsd_snd_probe(struct platform_device *pdev)
diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
index e52c032d53aa..033c07fb599c 100644
--- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
@@ -710,11 +710,12 @@ static int mt6797_afe_component_probe(struct snd_soc_component *component)
}
static const struct snd_soc_component_driver mt6797_afe_component = {
- .name = AFE_PCM_NAME,
- .ops = &mtk_afe_pcm_ops,
- .pcm_new = mtk_afe_pcm_new,
- .pcm_free = mtk_afe_pcm_free,
- .probe = mt6797_afe_component_probe,
+ .name = AFE_PCM_NAME,
+ .probe = mt6797_afe_component_probe,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .pointer = mtk_afe_pcm_pointer,
+ .pcm_construct = mtk_afe_pcm_new,
+ .pcm_destruct = mtk_afe_pcm_free,
};
static int mt6797_dai_memif_register(struct mtk_base_afe *afe)
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
index 4a31106d3471..76af09d8f1af 100644
--- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
@@ -11,6 +11,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include "mt8183-afe-common.h"
#include "mt8183-afe-clk.h"
@@ -1047,11 +1048,12 @@ static int mt8183_afe_component_probe(struct snd_soc_component *component)
}
static const struct snd_soc_component_driver mt8183_afe_component = {
- .name = AFE_PCM_NAME,
- .ops = &mtk_afe_pcm_ops,
- .pcm_new = mtk_afe_pcm_new,
- .pcm_free = mtk_afe_pcm_free,
- .probe = mt8183_afe_component_probe,
+ .name = AFE_PCM_NAME,
+ .probe = mt8183_afe_component_probe,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .pointer = mtk_afe_pcm_pointer,
+ .pcm_construct = mtk_afe_pcm_new,
+ .pcm_destruct = mtk_afe_pcm_free,
};
static int mt8183_dai_memif_register(struct mtk_base_afe *afe)
@@ -1089,6 +1091,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
struct mtk_base_afe *afe;
struct mt8183_afe_private *afe_priv;
struct device *dev;
+ struct reset_control *rstc;
int i, irq_id, ret;
afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
@@ -1126,6 +1129,19 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
return ret;
}
+ rstc = devm_reset_control_get(dev, "audiosys");
+ if (IS_ERR(rstc)) {
+ ret = PTR_ERR(rstc);
+ dev_err(dev, "could not get audiosys reset:%d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_reset(rstc);
+ if (ret) {
+ dev_err(dev, "failed to trigger audio reset:%d\n", ret);
+ return ret;
+ }
+
/* enable clock for regcache get default value from hw */
afe_priv->pm_runtime_bypass_reg_ctl = true;
pm_runtime_get_sync(&pdev->dev);
diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
index bb9cdc0d6552..0555f7d73d05 100644
--- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -19,11 +19,12 @@ enum PINCTRL_PIN_STATE {
PIN_STATE_DEFAULT = 0,
PIN_TDM_OUT_ON,
PIN_TDM_OUT_OFF,
+ PIN_WOV,
PIN_STATE_MAX
};
static const char * const mt8183_pin_str[PIN_STATE_MAX] = {
- "default", "aud_tdm_out_on", "aud_tdm_out_off",
+ "default", "aud_tdm_out_on", "aud_tdm_out_off", "wov",
};
struct mt8183_mt6358_ts3a227_max98357_priv {
@@ -142,6 +143,11 @@ SND_SOC_DAILINK_DEFS(playback_hdmi,
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(wake_on_voice,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
/* BE */
SND_SOC_DAILINK_DEFS(primary_codec,
DAILINK_COMP_ARRAY(COMP_CPU("ADDA")),
@@ -229,6 +235,41 @@ static struct snd_soc_ops mt8183_mt6358_tdm_ops = {
.shutdown = mt8183_mt6358_tdm_shutdown,
};
+static int
+mt8183_mt6358_ts3a227_max98357_wov_startup(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct mt8183_mt6358_ts3a227_max98357_priv *priv =
+ snd_soc_card_get_drvdata(card);
+
+ return pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_WOV]);
+}
+
+static void
+mt8183_mt6358_ts3a227_max98357_wov_shutdown(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct mt8183_mt6358_ts3a227_max98357_priv *priv =
+ snd_soc_card_get_drvdata(card);
+ int ret;
+
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_STATE_DEFAULT]);
+ if (ret)
+ dev_err(card->dev, "%s failed to select state %d\n",
+ __func__, ret);
+}
+
+static const struct snd_soc_ops mt8183_mt6358_ts3a227_max98357_wov_ops = {
+ .startup = mt8183_mt6358_ts3a227_max98357_wov_startup,
+ .shutdown = mt8183_mt6358_ts3a227_max98357_wov_shutdown,
+};
+
static struct snd_soc_dai_link
mt8183_mt6358_ts3a227_max98357_dai_links[] = {
/* FE */
@@ -306,6 +347,15 @@ mt8183_mt6358_ts3a227_max98357_dai_links[] = {
.dpcm_playback = 1,
SND_SOC_DAILINK_REG(playback_hdmi),
},
+ {
+ .name = "Wake on Voice",
+ .stream_name = "Wake on Voice",
+ .ignore_suspend = 1,
+ .ignore = 1,
+ SND_SOC_DAILINK_REG(wake_on_voice),
+ .ops = &mt8183_mt6358_ts3a227_max98357_wov_ops,
+ },
+
/* BE */
{
.name = "Primary Codec",
@@ -429,7 +479,7 @@ static int
mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8183_mt6358_ts3a227_max98357_card;
- struct device_node *platform_node;
+ struct device_node *platform_node, *ec_codec;
struct snd_soc_dai_link *dai_link;
struct mt8183_mt6358_ts3a227_max98357_priv *priv;
int ret;
@@ -444,10 +494,24 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
return -EINVAL;
}
+ ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0);
+
for_each_card_prelinks(card, i, dai_link) {
if (dai_link->platforms->name)
continue;
- dai_link->platforms->of_node = platform_node;
+
+ if (ec_codec && strcmp(dai_link->name, "Wake on Voice") == 0) {
+ dai_link->cpus[0].name = NULL;
+ dai_link->cpus[0].of_node = ec_codec;
+ dai_link->cpus[0].dai_name = NULL;
+ dai_link->codecs[0].name = NULL;
+ dai_link->codecs[0].of_node = ec_codec;
+ dai_link->codecs[0].dai_name = "Wake on Voice";
+ dai_link->platforms[0].of_node = ec_codec;
+ dai_link->ignore = 0;
+ } else {
+ dai_link->platforms->of_node = platform_node;
+ }
}
mt8183_mt6358_ts3a227_max98357_headset_dev.dlc.of_node =
diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c
index 5a3749938900..d6f3eefb8f09 100644
--- a/sound/soc/meson/axg-fifo.c
+++ b/sound/soc/meson/axg-fifo.c
@@ -70,7 +70,8 @@ static void __dma_enable(struct axg_fifo *fifo, bool enable)
enable ? CTRL0_DMA_EN : 0);
}
-static int axg_fifo_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
+int axg_fifo_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss, int cmd)
{
struct axg_fifo *fifo = axg_fifo_data(ss);
@@ -91,8 +92,10 @@ static int axg_fifo_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
return 0;
}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_trigger);
-static snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_pcm_substream *ss)
+snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct axg_fifo *fifo = axg_fifo_data(ss);
struct snd_pcm_runtime *runtime = ss->runtime;
@@ -102,9 +105,11 @@ static snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_pcm_substream *ss)
return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_pointer);
-static int axg_fifo_pcm_hw_params(struct snd_pcm_substream *ss,
- struct snd_pcm_hw_params *params)
+int axg_fifo_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = ss->runtime;
struct axg_fifo *fifo = axg_fifo_data(ss);
@@ -132,15 +137,17 @@ static int axg_fifo_pcm_hw_params(struct snd_pcm_substream *ss,
return 0;
}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_params);
-static int g12a_fifo_pcm_hw_params(struct snd_pcm_substream *ss,
- struct snd_pcm_hw_params *params)
+int g12a_fifo_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss,
+ struct snd_pcm_hw_params *params)
{
struct axg_fifo *fifo = axg_fifo_data(ss);
struct snd_pcm_runtime *runtime = ss->runtime;
int ret;
- ret = axg_fifo_pcm_hw_params(ss, params);
+ ret = axg_fifo_pcm_hw_params(component, ss, params);
if (ret)
return ret;
@@ -149,8 +156,10 @@ static int g12a_fifo_pcm_hw_params(struct snd_pcm_substream *ss,
return 0;
}
+EXPORT_SYMBOL_GPL(g12a_fifo_pcm_hw_params);
-static int axg_fifo_pcm_hw_free(struct snd_pcm_substream *ss)
+int axg_fifo_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct axg_fifo *fifo = axg_fifo_data(ss);
@@ -160,6 +169,7 @@ static int axg_fifo_pcm_hw_free(struct snd_pcm_substream *ss)
return snd_pcm_lib_free_pages(ss);
}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_free);
static void axg_fifo_ack_irq(struct axg_fifo *fifo, u8 mask)
{
@@ -194,7 +204,8 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id)
return IRQ_RETVAL(status);
}
-static int axg_fifo_pcm_open(struct snd_pcm_substream *ss)
+int axg_fifo_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct axg_fifo *fifo = axg_fifo_data(ss);
struct device *dev = axg_fifo_dev(ss);
@@ -250,8 +261,10 @@ static int axg_fifo_pcm_open(struct snd_pcm_substream *ss)
return ret;
}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_open);
-static int axg_fifo_pcm_close(struct snd_pcm_substream *ss)
+int axg_fifo_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct axg_fifo *fifo = axg_fifo_data(ss);
int ret;
@@ -267,28 +280,7 @@ static int axg_fifo_pcm_close(struct snd_pcm_substream *ss)
return ret;
}
-
-const struct snd_pcm_ops axg_fifo_pcm_ops = {
- .open = axg_fifo_pcm_open,
- .close = axg_fifo_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = axg_fifo_pcm_hw_params,
- .hw_free = axg_fifo_pcm_hw_free,
- .pointer = axg_fifo_pcm_pointer,
- .trigger = axg_fifo_pcm_trigger,
-};
-EXPORT_SYMBOL_GPL(axg_fifo_pcm_ops);
-
-const struct snd_pcm_ops g12a_fifo_pcm_ops = {
- .open = axg_fifo_pcm_open,
- .close = axg_fifo_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = g12a_fifo_pcm_hw_params,
- .hw_free = axg_fifo_pcm_hw_free,
- .pointer = axg_fifo_pcm_pointer,
- .trigger = axg_fifo_pcm_trigger,
-};
-EXPORT_SYMBOL_GPL(g12a_fifo_pcm_ops);
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_close);
int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type)
{
diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h
index bb1e2ce50256..cf928d43b558 100644
--- a/sound/soc/meson/axg-fifo.h
+++ b/sound/soc/meson/axg-fifo.h
@@ -15,7 +15,7 @@ struct reset_control;
struct snd_soc_component_driver;
struct snd_soc_dai;
struct snd_soc_dai_driver;
-struct snd_pcm_ops;
+
struct snd_soc_pcm_runtime;
#define AXG_FIFO_CH_MAX 128
@@ -75,8 +75,22 @@ struct axg_fifo_match_data {
struct snd_soc_dai_driver *dai_drv;
};
-extern const struct snd_pcm_ops axg_fifo_pcm_ops;
-extern const struct snd_pcm_ops g12a_fifo_pcm_ops;
+int axg_fifo_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss);
+int axg_fifo_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss);
+int axg_fifo_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss,
+ struct snd_pcm_hw_params *params);
+int g12a_fifo_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss,
+ struct snd_pcm_hw_params *params);
+int axg_fifo_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss);
+snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss);
+int axg_fifo_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss, int cmd);
int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type);
int axg_fifo_probe(struct platform_device *pdev);
diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c
index 6ab111c31b28..665d75d49d7b 100644
--- a/sound/soc/meson/axg-frddr.c
+++ b/sound/soc/meson/axg-frddr.c
@@ -149,7 +149,13 @@ static const struct snd_soc_component_driver axg_frddr_component_drv = {
.num_dapm_widgets = ARRAY_SIZE(axg_frddr_dapm_widgets),
.dapm_routes = axg_frddr_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(axg_frddr_dapm_routes),
- .ops = &axg_fifo_pcm_ops
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = axg_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
};
static const struct axg_fifo_match_data axg_frddr_match_data = {
@@ -267,7 +273,13 @@ static const struct snd_soc_component_driver g12a_frddr_component_drv = {
.num_dapm_widgets = ARRAY_SIZE(g12a_frddr_dapm_widgets),
.dapm_routes = g12a_frddr_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes),
- .ops = &g12a_fifo_pcm_ops
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = g12a_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
};
static const struct axg_fifo_match_data g12a_frddr_match_data = {
@@ -331,7 +343,13 @@ static const struct snd_soc_component_driver sm1_frddr_component_drv = {
.num_dapm_widgets = ARRAY_SIZE(sm1_frddr_dapm_widgets),
.dapm_routes = g12a_frddr_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes),
- .ops = &g12a_fifo_pcm_ops
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = g12a_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
};
static const struct axg_fifo_match_data sm1_frddr_match_data = {
diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c
index c8ea2145f576..7fef0b961496 100644
--- a/sound/soc/meson/axg-toddr.c
+++ b/sound/soc/meson/axg-toddr.c
@@ -181,7 +181,13 @@ static const struct snd_soc_component_driver axg_toddr_component_drv = {
.num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets),
.dapm_routes = axg_toddr_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes),
- .ops = &axg_fifo_pcm_ops
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = axg_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
};
static const struct axg_fifo_match_data axg_toddr_match_data = {
@@ -214,7 +220,13 @@ static const struct snd_soc_component_driver g12a_toddr_component_drv = {
.num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets),
.dapm_routes = axg_toddr_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes),
- .ops = &g12a_fifo_pcm_ops
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = g12a_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
};
static const struct axg_fifo_match_data g12a_toddr_match_data = {
@@ -278,7 +290,13 @@ static const struct snd_soc_component_driver sm1_toddr_component_drv = {
.num_dapm_widgets = ARRAY_SIZE(sm1_toddr_dapm_widgets),
.dapm_routes = sm1_toddr_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(sm1_toddr_dapm_routes),
- .ops = &g12a_fifo_pcm_ops
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = g12a_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
};
static const struct axg_fifo_match_data sm1_toddr_match_data = {
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 7096b5263e25..54a4c9213e83 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -55,8 +55,9 @@ static struct snd_pcm_hardware mmp_pcm_hardware[] = {
},
};
-static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int mmp_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
struct dma_slave_config slave_config;
@@ -77,6 +78,18 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int mmp_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ return snd_dmaengine_pcm_trigger(substream, cmd);
+}
+
+static snd_pcm_uframes_t mmp_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return snd_dmaengine_pcm_pointer(substream);
+}
+
static bool filter(struct dma_chan *chan, void *param)
{
struct mmp_dma_data *dma_data = param;
@@ -94,10 +107,10 @@ static bool filter(struct dma_chan *chan, void *param)
return found;
}
-static int mmp_pcm_open(struct snd_pcm_substream *substream)
+static int mmp_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct platform_device *pdev = to_platform_device(component->dev);
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct mmp_dma_data dma_data;
@@ -117,8 +130,15 @@ static int mmp_pcm_open(struct snd_pcm_substream *substream)
&dma_data);
}
-static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static int mmp_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return snd_dmaengine_pcm_close_release_chan(substream);
+}
+
+static int mmp_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long off = vma->vm_pgoff;
@@ -129,17 +149,8 @@ static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
-static const struct snd_pcm_ops mmp_pcm_ops = {
- .open = mmp_pcm_open,
- .close = snd_dmaengine_pcm_close_release_chan,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = mmp_pcm_hw_params,
- .trigger = snd_dmaengine_pcm_trigger,
- .pointer = snd_dmaengine_pcm_pointer,
- .mmap = mmp_pcm_mmap,
-};
-
-static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
+static void mmp_pcm_free_dma_buffers(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
@@ -188,7 +199,8 @@ static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
return 0;
}
-static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int mmp_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm_substream *substream;
struct snd_pcm *pcm = rtd->pcm;
@@ -205,15 +217,21 @@ static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
err:
- mmp_pcm_free_dma_buffers(pcm);
+ mmp_pcm_free_dma_buffers(component, pcm);
return ret;
}
static const struct snd_soc_component_driver mmp_soc_component = {
.name = DRV_NAME,
- .ops = &mmp_pcm_ops,
- .pcm_new = mmp_pcm_new,
- .pcm_free = mmp_pcm_free_dma_buffers,
+ .open = mmp_pcm_open,
+ .close = mmp_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = mmp_pcm_hw_params,
+ .trigger = mmp_pcm_trigger,
+ .pointer = mmp_pcm_pointer,
+ .mmap = mmp_pcm_mmap,
+ .pcm_construct = mmp_pcm_new,
+ .pcm_destruct = mmp_pcm_free_dma_buffers,
};
static int mmp_pcm_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 48d5c2252b10..59ef04d0467a 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -56,7 +56,7 @@ static void poodle_ext_control(struct snd_soc_dapm_context *dapm)
snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
}
- /* set the enpoints to their new connetion states */
+ /* set the endpoints to their new connection states */
if (poodle_spk_func == POODLE_SPK_ON)
snd_soc_dapm_enable_pin(dapm, "Ext Spk");
else
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 5fdd1a24c232..49cb3ba1519f 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -869,9 +869,17 @@ static struct snd_soc_dai_driver pxa_ssp_dai = {
static const struct snd_soc_component_driver pxa_ssp_component = {
.name = "pxa-ssp",
- .ops = &pxa2xx_pcm_ops,
- .pcm_new = pxa2xx_soc_pcm_new,
- .pcm_free = pxa2xx_pcm_free_dma_buffers,
+ .pcm_construct = pxa2xx_soc_pcm_new,
+ .pcm_destruct = pxa2xx_soc_pcm_free,
+ .open = pxa2xx_soc_pcm_open,
+ .close = pxa2xx_soc_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = pxa2xx_soc_pcm_hw_params,
+ .hw_free = pxa2xx_soc_pcm_hw_free,
+ .prepare = pxa2xx_soc_pcm_prepare,
+ .trigger = pxa2xx_soc_pcm_trigger,
+ .pointer = pxa2xx_soc_pcm_pointer,
+ .mmap = pxa2xx_soc_pcm_mmap,
};
#ifdef CONFIG_OF
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index bf28187315db..31e81a6f616f 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -204,9 +204,17 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
static const struct snd_soc_component_driver pxa_ac97_component = {
.name = "pxa-ac97",
- .ops = &pxa2xx_pcm_ops,
- .pcm_new = pxa2xx_soc_pcm_new,
- .pcm_free = pxa2xx_pcm_free_dma_buffers,
+ .pcm_construct = pxa2xx_soc_pcm_new,
+ .pcm_destruct = pxa2xx_soc_pcm_free,
+ .open = pxa2xx_soc_pcm_open,
+ .close = pxa2xx_soc_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = pxa2xx_soc_pcm_hw_params,
+ .hw_free = pxa2xx_soc_pcm_hw_free,
+ .prepare = pxa2xx_soc_pcm_prepare,
+ .trigger = pxa2xx_soc_pcm_trigger,
+ .pointer = pxa2xx_soc_pcm_pointer,
+ .mmap = pxa2xx_soc_pcm_mmap,
};
#ifdef CONFIG_OF
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 9f7fb7335ac0..e77d707efde7 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -360,9 +360,17 @@ static struct snd_soc_dai_driver pxa_i2s_dai = {
static const struct snd_soc_component_driver pxa_i2s_component = {
.name = "pxa-i2s",
- .ops = &pxa2xx_pcm_ops,
- .pcm_new = pxa2xx_soc_pcm_new,
- .pcm_free = pxa2xx_pcm_free_dma_buffers,
+ .pcm_construct = pxa2xx_soc_pcm_new,
+ .pcm_destruct = pxa2xx_soc_pcm_free,
+ .open = pxa2xx_soc_pcm_open,
+ .close = pxa2xx_soc_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = pxa2xx_soc_pcm_hw_params,
+ .hw_free = pxa2xx_soc_pcm_hw_free,
+ .prepare = pxa2xx_soc_pcm_prepare,
+ .trigger = pxa2xx_soc_pcm_trigger,
+ .pointer = pxa2xx_soc_pcm_pointer,
+ .mmap = pxa2xx_soc_pcm_mmap,
};
static int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 74b56fa0870f..07b3455a6f23 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -18,9 +18,17 @@
#include <sound/dmaengine_pcm.h>
static const struct snd_soc_component_driver pxa2xx_soc_platform = {
- .ops = &pxa2xx_pcm_ops,
- .pcm_new = pxa2xx_soc_pcm_new,
- .pcm_free = pxa2xx_pcm_free_dma_buffers,
+ .pcm_construct = pxa2xx_soc_pcm_new,
+ .pcm_destruct = pxa2xx_soc_pcm_free,
+ .open = pxa2xx_soc_pcm_open,
+ .close = pxa2xx_soc_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = pxa2xx_soc_pcm_hw_params,
+ .hw_free = pxa2xx_soc_pcm_hw_free,
+ .prepare = pxa2xx_soc_pcm_prepare,
+ .trigger = pxa2xx_soc_pcm_trigger,
+ .pointer = pxa2xx_soc_pcm_pointer,
+ .mmap = pxa2xx_soc_pcm_mmap,
};
static int pxa2xx_soc_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 4c745baa39f7..2e8892316423 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -50,12 +50,12 @@ static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
.fifo_size = 0,
};
-static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
+static int lpass_platform_pcmops_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct lpass_variant *v = drvdata->variant;
int ret, dma_ch, dir = substream->stream;
@@ -105,11 +105,10 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
return 0;
}
-static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
+static int lpass_platform_pcmops_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct lpass_variant *v = drvdata->variant;
struct lpass_pcm_data *data;
@@ -122,11 +121,11 @@ static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
return 0;
}
-static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *rt = substream->runtime;
struct lpass_pcm_data *pcm_data = rt->private_data;
@@ -216,10 +215,10 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
+static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *rt = substream->runtime;
struct lpass_pcm_data *pcm_data = rt->private_data;
@@ -236,11 +235,11 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
return ret;
}
-static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
+static int lpass_platform_pcmops_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *rt = substream->runtime;
struct lpass_pcm_data *pcm_data = rt->private_data;
@@ -288,11 +287,11 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
- int cmd)
+static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ int cmd)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *rt = substream->runtime;
struct lpass_pcm_data *pcm_data = rt->private_data;
@@ -363,10 +362,10 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
}
static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
+ struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *rt = substream->runtime;
struct lpass_pcm_data *pcm_data = rt->private_data;
@@ -395,8 +394,9 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
return bytes_to_frames(substream->runtime, curr_addr - base_addr);
}
-static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static int lpass_platform_pcmops_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -405,18 +405,6 @@ static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
runtime->dma_bytes);
}
-static const struct snd_pcm_ops lpass_platform_pcm_ops = {
- .open = lpass_platform_pcmops_open,
- .close = lpass_platform_pcmops_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = lpass_platform_pcmops_hw_params,
- .hw_free = lpass_platform_pcmops_hw_free,
- .prepare = lpass_platform_pcmops_prepare,
- .trigger = lpass_platform_pcmops_trigger,
- .pointer = lpass_platform_pcmops_pointer,
- .mmap = lpass_platform_pcmops_mmap,
-};
-
static irqreturn_t lpass_dma_interrupt_handler(
struct snd_pcm_substream *substream,
struct lpass_data *drvdata,
@@ -499,11 +487,11 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
+static int lpass_platform_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *soc_runtime)
{
struct snd_pcm *pcm = soc_runtime->pcm;
struct snd_pcm_substream *psubstream, *csubstream;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
int ret = -EINVAL;
size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
@@ -535,7 +523,8 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
return 0;
}
-static void lpass_platform_pcm_free(struct snd_pcm *pcm)
+static void lpass_platform_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
int i;
@@ -552,9 +541,18 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
static const struct snd_soc_component_driver lpass_component_driver = {
.name = DRV_NAME,
- .pcm_new = lpass_platform_pcm_new,
- .pcm_free = lpass_platform_pcm_free,
- .ops = &lpass_platform_pcm_ops,
+ .open = lpass_platform_pcmops_open,
+ .close = lpass_platform_pcmops_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = lpass_platform_pcmops_hw_params,
+ .hw_free = lpass_platform_pcmops_hw_free,
+ .prepare = lpass_platform_pcmops_prepare,
+ .trigger = lpass_platform_pcmops_trigger,
+ .pointer = lpass_platform_pcmops_pointer,
+ .mmap = lpass_platform_pcmops_mmap,
+ .pcm_construct = lpass_platform_pcm_new,
+ .pcm_destruct = lpass_platform_pcm_free,
+
};
int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
index 548eb4fa2da6..8150c10f081e 100644
--- a/sound/soc/qcom/qdsp6/q6asm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -206,16 +206,16 @@ static void event_handler(uint32_t opcode, uint32_t token,
}
}
-static int q6asm_dai_prepare(struct snd_pcm_substream *substream)
+static int q6asm_dai_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct q6asm_dai_rtd *prtd = runtime->private_data;
- struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME);
struct q6asm_dai_data *pdata;
int ret, i;
- pdata = snd_soc_component_get_drvdata(c);
+ pdata = snd_soc_component_get_drvdata(component);
if (!pdata)
return -EINVAL;
@@ -294,7 +294,8 @@ static int q6asm_dai_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int q6asm_dai_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -322,21 +323,21 @@ static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
-static int q6asm_dai_open(struct snd_pcm_substream *substream)
+static int q6asm_dai_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct snd_soc_dai *cpu_dai = soc_prtd->cpu_dai;
- struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME);
struct q6asm_dai_rtd *prtd;
struct q6asm_dai_data *pdata;
- struct device *dev = c->dev;
+ struct device *dev = component->dev;
int ret = 0;
int stream_id;
stream_id = cpu_dai->driver->id;
- pdata = snd_soc_component_get_drvdata(c);
+ pdata = snd_soc_component_get_drvdata(component);
if (!pdata) {
pr_err("Drv data not found ..\n");
return -EINVAL;
@@ -414,7 +415,8 @@ static int q6asm_dai_open(struct snd_pcm_substream *substream)
return 0;
}
-static int q6asm_dai_close(struct snd_pcm_substream *substream)
+static int q6asm_dai_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
@@ -435,7 +437,8 @@ static int q6asm_dai_close(struct snd_pcm_substream *substream)
return 0;
}
-static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -447,22 +450,21 @@ static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
}
-static int q6asm_dai_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static int q6asm_dai_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
{
-
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
- struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME);
- struct device *dev = c->dev;
+ struct device *dev = component->dev;
return dma_mmap_coherent(dev, vma,
runtime->dma_area, runtime->dma_addr,
runtime->dma_bytes);
}
-static int q6asm_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int q6asm_dai_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct q6asm_dai_rtd *prtd = runtime->private_data;
@@ -482,17 +484,6 @@ static int q6asm_dai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_pcm_ops q6asm_dai_ops = {
- .open = q6asm_dai_open,
- .hw_params = q6asm_dai_hw_params,
- .close = q6asm_dai_close,
- .ioctl = snd_pcm_lib_ioctl,
- .prepare = q6asm_dai_prepare,
- .trigger = q6asm_dai_trigger,
- .pointer = q6asm_dai_pointer,
- .mmap = q6asm_dai_mmap,
-};
-
static void compress_event_handler(uint32_t opcode, uint32_t token,
uint32_t *payload, void *priv)
{
@@ -635,8 +626,14 @@ static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream,
struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
int dir = stream->direction;
struct q6asm_dai_data *pdata;
+ struct q6asm_flac_cfg flac_cfg;
struct device *dev = c->dev;
int ret;
+ union snd_codec_options *codec_options;
+ struct snd_dec_flac *flac;
+
+ codec_options = &(prtd->codec_param.codec.options);
+
memcpy(&prtd->codec_param, params, sizeof(*params));
@@ -673,6 +670,32 @@ static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream,
return ret;
}
+ switch (params->codec.id) {
+ case SND_AUDIOCODEC_FLAC:
+
+ memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg));
+ flac = &codec_options->flac_d;
+
+ flac_cfg.ch_cfg = params->codec.ch_in;
+ flac_cfg.sample_rate = params->codec.sample_rate;
+ flac_cfg.stream_info_present = 1;
+ flac_cfg.sample_size = flac->sample_size;
+ flac_cfg.min_blk_size = flac->min_blk_size;
+ flac_cfg.max_blk_size = flac->max_blk_size;
+ flac_cfg.max_frame_size = flac->max_frame_size;
+ flac_cfg.min_frame_size = flac->min_frame_size;
+
+ ret = q6asm_stream_media_format_block_flac(prtd->audio_client,
+ &flac_cfg);
+ if (ret < 0) {
+ dev_err(dev, "FLAC CMD Format block failed:%d\n", ret);
+ return -EIO;
+ }
+ break;
+ default:
+ break;
+ }
+
ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys,
(prtd->pcm_size / prtd->periods),
prtd->periods);
@@ -768,8 +791,9 @@ static int q6asm_dai_compr_get_caps(struct snd_compr_stream *stream,
caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE;
caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;
caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
- caps->num_codecs = 1;
+ caps->num_codecs = 2;
caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_FLAC;
return 0;
}
@@ -800,15 +824,15 @@ static struct snd_compr_ops q6asm_dai_compr_ops = {
.ack = q6asm_dai_compr_ack,
};
-static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int q6asm_dai_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm_substream *psubstream, *csubstream;
- struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_pcm *pcm = rtd->pcm;
struct device *dev;
int size, ret;
- dev = c->dev;
+ dev = component->dev;
size = q6asm_dai_hardware_playback.buffer_bytes_max;
psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
if (psubstream) {
@@ -835,7 +859,8 @@ static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static void q6asm_dai_pcm_free(struct snd_pcm *pcm)
+static void q6asm_dai_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
int i;
@@ -852,9 +877,16 @@ static void q6asm_dai_pcm_free(struct snd_pcm *pcm)
static const struct snd_soc_component_driver q6asm_fe_dai_component = {
.name = DRV_NAME,
- .ops = &q6asm_dai_ops,
- .pcm_new = q6asm_dai_pcm_new,
- .pcm_free = q6asm_dai_pcm_free,
+ .open = q6asm_dai_open,
+ .hw_params = q6asm_dai_hw_params,
+ .close = q6asm_dai_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .prepare = q6asm_dai_prepare,
+ .trigger = q6asm_dai_trigger,
+ .pointer = q6asm_dai_pointer,
+ .mmap = q6asm_dai_mmap,
+ .pcm_construct = q6asm_dai_pcm_new,
+ .pcm_destruct = q6asm_dai_pcm_free,
.compr_ops = &q6asm_dai_compr_ops,
};
diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
index e8141a33a55e..36e0eab13a98 100644
--- a/sound/soc/qcom/qdsp6/q6asm.c
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -38,6 +38,7 @@
#define ASM_SESSION_CMD_RUN_V2 0x00010DAA
#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5
#define ASM_MEDIA_FMT_MP3 0x00010BE9
+#define ASM_MEDIA_FMT_FLAC 0x00010C16
#define ASM_DATA_CMD_WRITE_V2 0x00010DAB
#define ASM_DATA_CMD_READ_V2 0x00010DAC
#define ASM_SESSION_CMD_SUSPEND 0x00010DEC
@@ -89,6 +90,20 @@ struct asm_multi_channel_pcm_fmt_blk_v2 {
u8 channel_mapping[PCM_MAX_NUM_CHANNEL];
} __packed;
+struct asm_flac_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 is_stream_info_present;
+ u16 num_channels;
+ u16 min_blk_size;
+ u16 max_blk_size;
+ u16 md5_sum[8];
+ u32 sample_rate;
+ u32 min_frame_size;
+ u32 max_frame_size;
+ u16 sample_size;
+ u16 reserved;
+} __packed;
+
struct asm_stream_cmd_set_encdec_param {
u32 param_id;
u32 param_size;
@@ -876,6 +891,9 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format,
case FORMAT_LINEAR_PCM:
open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
break;
+ case SND_AUDIOCODEC_FLAC:
+ open->dec_fmt_id = ASM_MEDIA_FMT_FLAC;
+ break;
default:
dev_err(ac->dev, "Invalid format 0x%x\n", format);
rc = -EINVAL;
@@ -1021,6 +1039,42 @@ err:
}
EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm);
+
+int q6asm_stream_media_format_block_flac(struct audio_client *ac,
+ struct q6asm_flac_cfg *cfg)
+{
+ struct asm_flac_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ void *p;
+ int rc, pkt_size;
+
+ pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ p = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+ fmt->is_stream_info_present = cfg->stream_info_present;
+ fmt->num_channels = cfg->ch_cfg;
+ fmt->min_blk_size = cfg->min_blk_size;
+ fmt->max_blk_size = cfg->max_blk_size;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->min_frame_size = cfg->min_frame_size;
+ fmt->max_frame_size = cfg->max_frame_size;
+ fmt->sample_size = cfg->sample_size;
+
+ rc = q6asm_ac_send_cmd_sync(ac, pkt);
+ kfree(pkt);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac);
/**
* q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture
*
@@ -1075,6 +1129,7 @@ err:
}
EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support);
+
/**
* q6asm_read() - read data of period size from audio client
*
diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h
index 9f5fb573e4a0..6764f55f7078 100644
--- a/sound/soc/qcom/qdsp6/q6asm.h
+++ b/sound/soc/qcom/qdsp6/q6asm.h
@@ -32,6 +32,19 @@ enum {
#define NO_TIMESTAMP 0xFF00
#define FORMAT_LINEAR_PCM 0x0000
+struct q6asm_flac_cfg {
+ u32 sample_rate;
+ u32 ext_sample_rate;
+ u32 min_frame_size;
+ u32 max_frame_size;
+ u16 stream_info_present;
+ u16 min_blk_size;
+ u16 max_blk_size;
+ u16 ch_cfg;
+ u16 sample_size;
+ u16 md5_sum;
+};
+
typedef void (*q6asm_cb) (uint32_t opcode, uint32_t token,
void *payload, void *priv);
struct audio_client;
@@ -54,6 +67,8 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
uint32_t rate, uint32_t channels,
u8 channel_map[PCM_MAX_NUM_CHANNEL],
uint16_t bits_per_sample);
+int q6asm_stream_media_format_block_flac(struct audio_client *ac,
+ struct q6asm_flac_cfg *cfg);
int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts,
uint32_t lsw_ts);
int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts,
diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c
index ddcd9978cf57..20724102e85a 100644
--- a/sound/soc/qcom/qdsp6/q6routing.c
+++ b/sound/soc/qcom/qdsp6/q6routing.c
@@ -939,12 +939,12 @@ static const struct snd_soc_dapm_route intercon[] = {
};
-static int routing_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int routing_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
- struct msm_routing_data *data = dev_get_drvdata(c->dev);
+ struct msm_routing_data *data = dev_get_drvdata(component->dev);
unsigned int be_id = rtd->cpu_dai->id;
struct session_data *session;
int path_type;
@@ -980,10 +980,6 @@ static int routing_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_pcm_ops q6pcm_routing_ops = {
- .hw_params = routing_hw_params,
-};
-
static int msm_routing_probe(struct snd_soc_component *c)
{
int i;
@@ -997,9 +993,9 @@ static int msm_routing_probe(struct snd_soc_component *c)
}
static const struct snd_soc_component_driver msm_soc_routing_component = {
- .ops = &q6pcm_routing_ops,
.probe = msm_routing_probe,
.name = DRV_NAME,
+ .hw_params = routing_hw_params,
.dapm_widgets = msm_qdsp6_widgets,
.num_dapm_widgets = ARRAY_SIZE(msm_qdsp6_widgets),
.dapm_routes = intercon,
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index b43657e6e655..d610b553ea3b 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -40,9 +40,10 @@ config SND_SOC_ROCKCHIP_MAX98090
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
+ select SND_SOC_HDMI_CODEC
help
Say Y or M here if you want to add support for SoC audio on Rockchip
- boards using the MAX98090 codec, such as Veyron.
+ boards using the MAX98090 codec and HDMI codec, such as Veyron.
config SND_SOC_ROCKCHIP_RT5645
tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c
index e80b09143b63..60930fa85aa4 100644
--- a/sound/soc/rockchip/rockchip_max98090.c
+++ b/sound/soc/rockchip/rockchip_max98090.c
@@ -6,11 +6,13 @@
*/
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <sound/core.h>
+#include <sound/hdmi-codec.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -36,28 +38,73 @@ static struct snd_soc_jack_pin headset_jack_pins[] = {
};
-static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
+#define RK_MAX98090_WIDGETS \
+ SND_SOC_DAPM_HP("Headphone", NULL), \
+ SND_SOC_DAPM_MIC("Headset Mic", NULL), \
+ SND_SOC_DAPM_MIC("Int Mic", NULL), \
+ SND_SOC_DAPM_SPK("Speaker", NULL)
+
+#define RK_HDMI_WIDGETS \
+ SND_SOC_DAPM_LINE("HDMI", NULL)
+
+static const struct snd_soc_dapm_widget rk_max98090_dapm_widgets[] = {
+ RK_MAX98090_WIDGETS,
+};
+
+static const struct snd_soc_dapm_widget rk_hdmi_dapm_widgets[] = {
+ RK_HDMI_WIDGETS,
+};
+
+static const struct snd_soc_dapm_widget rk_max98090_hdmi_dapm_widgets[] = {
+ RK_MAX98090_WIDGETS,
+ RK_HDMI_WIDGETS,
+};
+
+#define RK_MAX98090_AUDIO_MAP \
+ {"IN34", NULL, "Headset Mic"}, \
+ {"Headset Mic", NULL, "MICBIAS"}, \
+ {"DMICL", NULL, "Int Mic"}, \
+ {"Headphone", NULL, "HPL"}, \
+ {"Headphone", NULL, "HPR"}, \
+ {"Speaker", NULL, "SPKL"}, \
+ {"Speaker", NULL, "SPKR"}
+
+#define RK_HDMI_AUDIO_MAP \
+ {"HDMI", NULL, "TX"}
+
+static const struct snd_soc_dapm_route rk_max98090_audio_map[] = {
+ RK_MAX98090_AUDIO_MAP,
+};
+
+static const struct snd_soc_dapm_route rk_hdmi_audio_map[] = {
+ RK_HDMI_AUDIO_MAP,
+};
+
+static const struct snd_soc_dapm_route rk_max98090_hdmi_audio_map[] = {
+ RK_MAX98090_AUDIO_MAP,
+ RK_HDMI_AUDIO_MAP,
+};
+
+#define RK_MAX98090_CONTROLS \
+ SOC_DAPM_PIN_SWITCH("Headphone"), \
+ SOC_DAPM_PIN_SWITCH("Headset Mic"), \
+ SOC_DAPM_PIN_SWITCH("Int Mic"), \
+ SOC_DAPM_PIN_SWITCH("Speaker")
+
+#define RK_HDMI_CONTROLS \
+ SOC_DAPM_PIN_SWITCH("HDMI")
+
+static const struct snd_kcontrol_new rk_max98090_controls[] = {
+ RK_MAX98090_CONTROLS,
};
-static const struct snd_soc_dapm_route rk_audio_map[] = {
- {"IN34", NULL, "Headset Mic"},
- {"Headset Mic", NULL, "MICBIAS"},
- {"DMICL", NULL, "Int Mic"},
- {"Headphone", NULL, "HPL"},
- {"Headphone", NULL, "HPR"},
- {"Speaker", NULL, "SPKL"},
- {"Speaker", NULL, "SPKR"},
+static const struct snd_kcontrol_new rk_hdmi_controls[] = {
+ RK_HDMI_CONTROLS,
};
-static const struct snd_kcontrol_new rk_mc_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Int Mic"),
- SOC_DAPM_PIN_SWITCH("Speaker"),
+static const struct snd_kcontrol_new rk_max98090_hdmi_controls[] = {
+ RK_MAX98090_CONTROLS,
+ RK_HDMI_CONTROLS,
};
static int rk_jack_event(struct notifier_block *nb, unsigned long event,
@@ -125,15 +172,20 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
- if (ret < 0) {
- dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+ if (ret) {
+ dev_err(cpu_dai->dev, "Can't set cpu dai clock %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+
+ /* HDMI codec dai does not need to set sysclk. */
+ if (!strcmp(rtd->dai_link->name, "HDMI"))
+ return 0;
+
+ if (ret) {
+ dev_err(codec_dai->dev, "Can't set codec dai clock %d\n", ret);
return ret;
}
@@ -155,20 +207,88 @@ static const struct snd_soc_ops rk_aif1_ops = {
.startup = rk_aif1_startup,
};
-SND_SOC_DAILINK_DEFS(hifi,
- DAILINK_COMP_ARRAY(COMP_EMPTY()),
- DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-static struct snd_soc_dai_link rk_dailink = {
- .name = "max98090",
- .stream_name = "Audio",
- .init = rk_init,
- .ops = &rk_aif1_ops,
- /* set max98090 as slave */
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- SND_SOC_DAILINK_REG(hifi),
+SND_SOC_DAILINK_DEFS(analog,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hdmi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+enum {
+ DAILINK_MAX98090,
+ DAILINK_HDMI,
+};
+
+static struct snd_soc_jack rk_hdmi_jack;
+
+static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_component *component = runtime->codec_dai->component;
+ int ret;
+
+ /* enable jack detection */
+ ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
+ &rk_hdmi_jack, NULL, 0);
+ if (ret) {
+ dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
+ return ret;
+ }
+
+ return hdmi_codec_set_jack_detect(component, &rk_hdmi_jack);
+}
+
+/* max98090 dai_link */
+static struct snd_soc_dai_link rk_max98090_dailinks[] = {
+ {
+ .name = "max98090",
+ .stream_name = "Analog",
+ .init = rk_init,
+ .ops = &rk_aif1_ops,
+ /* set max98090 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAILINK_REG(analog),
+ },
+};
+
+/* HDMI codec dai_link */
+static struct snd_soc_dai_link rk_hdmi_dailinks[] = {
+ {
+ .name = "HDMI",
+ .stream_name = "HDMI",
+ .init = rk_hdmi_init,
+ .ops = &rk_aif1_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAILINK_REG(hdmi),
+ }
+};
+
+/* max98090 and HDMI codec dai_link */
+static struct snd_soc_dai_link rk_max98090_hdmi_dailinks[] = {
+ [DAILINK_MAX98090] = {
+ .name = "max98090",
+ .stream_name = "Analog",
+ .init = rk_init,
+ .ops = &rk_aif1_ops,
+ /* set max98090 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAILINK_REG(analog),
+ },
+ [DAILINK_HDMI] = {
+ .name = "HDMI",
+ .stream_name = "HDMI",
+ .init = rk_hdmi_init,
+ .ops = &rk_aif1_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAILINK_REG(hdmi),
+ }
};
static int rk_98090_headset_init(struct snd_soc_component *component);
@@ -178,19 +298,47 @@ static struct snd_soc_aux_dev rk_98090_headset_dev = {
.init = rk_98090_headset_init,
};
-static struct snd_soc_card snd_soc_card_rk = {
+static struct snd_soc_card rockchip_max98090_card = {
.name = "ROCKCHIP-I2S",
.owner = THIS_MODULE,
- .dai_link = &rk_dailink,
- .num_links = 1,
+ .dai_link = rk_max98090_dailinks,
+ .num_links = ARRAY_SIZE(rk_max98090_dailinks),
+ .aux_dev = &rk_98090_headset_dev,
+ .num_aux_devs = 1,
+ .dapm_widgets = rk_max98090_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_max98090_dapm_widgets),
+ .dapm_routes = rk_max98090_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rk_max98090_audio_map),
+ .controls = rk_max98090_controls,
+ .num_controls = ARRAY_SIZE(rk_max98090_controls),
+};
+
+static struct snd_soc_card rockchip_hdmi_card = {
+ .name = "ROCKCHIP-HDMI",
+ .owner = THIS_MODULE,
+ .dai_link = rk_hdmi_dailinks,
+ .num_links = ARRAY_SIZE(rk_hdmi_dailinks),
+ .dapm_widgets = rk_hdmi_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_hdmi_dapm_widgets),
+ .dapm_routes = rk_hdmi_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rk_hdmi_audio_map),
+ .controls = rk_hdmi_controls,
+ .num_controls = ARRAY_SIZE(rk_hdmi_controls),
+};
+
+static struct snd_soc_card rockchip_max98090_hdmi_card = {
+ .name = "ROCKCHIP-MAX98090-HDMI",
+ .owner = THIS_MODULE,
+ .dai_link = rk_max98090_hdmi_dailinks,
+ .num_links = ARRAY_SIZE(rk_max98090_hdmi_dailinks),
.aux_dev = &rk_98090_headset_dev,
.num_aux_devs = 1,
- .dapm_widgets = rk_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
- .dapm_routes = rk_audio_map,
- .num_dapm_routes = ARRAY_SIZE(rk_audio_map),
- .controls = rk_mc_controls,
- .num_controls = ARRAY_SIZE(rk_mc_controls),
+ .dapm_widgets = rk_max98090_hdmi_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_max98090_hdmi_dapm_widgets),
+ .dapm_routes = rk_max98090_hdmi_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rk_max98090_hdmi_audio_map),
+ .controls = rk_max98090_hdmi_controls,
+ .num_controls = ARRAY_SIZE(rk_max98090_hdmi_controls),
};
static int rk_98090_headset_init(struct snd_soc_component *component)
@@ -198,7 +346,7 @@ static int rk_98090_headset_init(struct snd_soc_component *component)
int ret;
/* Enable Headset and 4 Buttons Jack detection */
- ret = snd_soc_card_jack_new(&snd_soc_card_rk, "Headset Jack",
+ ret = snd_soc_card_jack_new(component->card, "Headset Jack",
SND_JACK_HEADSET |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
@@ -213,41 +361,75 @@ static int rk_98090_headset_init(struct snd_soc_component *component)
return ret;
}
+static int rk_parse_headset_from_of(struct device *dev, struct device_node *np)
+{
+ rk_98090_headset_dev.dlc.of_node = of_parse_phandle(
+ np, "rockchip,headset-codec", 0);
+ if (!rk_98090_headset_dev.dlc.of_node) {
+ dev_err(dev,
+ "Property 'rockchip,headset-codec' missing/invalid\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int snd_rk_mc_probe(struct platform_device *pdev)
{
int ret = 0;
- struct snd_soc_card *card = &snd_soc_card_rk;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
+ struct device_node *np_cpu;
+ struct device_node *np_audio, *np_hdmi;
- /* register the soc card */
- card->dev = &pdev->dev;
+ /* Parse DTS for I2S controller. */
+ np_cpu = of_parse_phandle(np, "rockchip,i2s-controller", 0);
- rk_dailink.codecs->of_node = of_parse_phandle(np,
- "rockchip,audio-codec", 0);
- if (!rk_dailink.codecs->of_node) {
+ if (!np_cpu) {
dev_err(&pdev->dev,
- "Property 'rockchip,audio-codec' missing or invalid\n");
+ "Property 'rockchip,i2s-controller missing or invalid\n");
return -EINVAL;
}
- rk_dailink.cpus->of_node = of_parse_phandle(np,
- "rockchip,i2s-controller", 0);
- if (!rk_dailink.cpus->of_node) {
- dev_err(&pdev->dev,
- "Property 'rockchip,i2s-controller' missing or invalid\n");
+ /*
+ * Find the card to use based on the presences of audio codec
+ * and hdmi codec in device property. Set their of_node accordingly.
+ */
+ np_audio = of_parse_phandle(np, "rockchip,audio-codec", 0);
+ np_hdmi = of_parse_phandle(np, "rockchip,hdmi-codec", 0);
+ if (np_audio && np_hdmi) {
+ card = &rockchip_max98090_hdmi_card;
+ card->dai_link[DAILINK_MAX98090].codecs->of_node = np_audio;
+ card->dai_link[DAILINK_HDMI].codecs->of_node = np_hdmi;
+ card->dai_link[DAILINK_MAX98090].cpus->of_node = np_cpu;
+ card->dai_link[DAILINK_MAX98090].platforms->of_node = np_cpu;
+ card->dai_link[DAILINK_HDMI].cpus->of_node = np_cpu;
+ card->dai_link[DAILINK_HDMI].platforms->of_node = np_cpu;
+ } else if (np_audio) {
+ card = &rockchip_max98090_card;
+ card->dai_link[0].codecs->of_node = np_audio;
+ card->dai_link[0].cpus->of_node = np_cpu;
+ card->dai_link[0].platforms->of_node = np_cpu;
+ } else if (np_hdmi) {
+ card = &rockchip_hdmi_card;
+ card->dai_link[0].codecs->of_node = np_hdmi;
+ card->dai_link[0].cpus->of_node = np_cpu;
+ card->dai_link[0].platforms->of_node = np_cpu;
+ } else {
+ dev_err(dev, "At least one of codecs should be specified\n");
return -EINVAL;
}
- rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;
+ card->dev = dev;
- rk_98090_headset_dev.dlc.of_node = of_parse_phandle(np,
- "rockchip,headset-codec", 0);
- if (!rk_98090_headset_dev.dlc.of_node) {
- dev_err(&pdev->dev,
- "Property 'rockchip,headset-codec' missing/invalid\n");
- return -EINVAL;
+ /* Parse headset detection codec. */
+ if (np_audio) {
+ ret = rk_parse_headset_from_of(dev, np);
+ if (ret)
+ return ret;
}
+ /* Parse card name. */
ret = snd_soc_of_parse_card_name(card, "rockchip,model");
if (ret) {
dev_err(&pdev->dev,
@@ -255,6 +437,7 @@ static int snd_rk_mc_probe(struct platform_device *pdev)
return ret;
}
+ /* register the soc card */
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 638983123d8f..1a0b163ca47b 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -194,11 +194,13 @@ config SND_SOC_ODROID
help
Say Y here to enable audio support for the Odroid XU3/XU4.
-config SND_SOC_ARNDALE_RT5631_ALC5631
- tristate "Audio support for RT5631(ALC5631) on Arndale Board"
- depends on I2C
- select SND_SAMSUNG_I2S
- select SND_SOC_RT5631
+config SND_SOC_ARNDALE
+ tristate "Audio support for Arndale Board"
+ depends on I2C
+ select SND_SAMSUNG_I2S
+ select SND_SOC_RT5631
+ select MFD_WM8994
+ select SND_SOC_WM8994
config SND_SOC_SAMSUNG_TM2_WM5110
tristate "SoC I2S Audio support for WM5110 on TM2 board"
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index c3b76035f69c..8f5dfe20b9f1 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -39,7 +39,7 @@ snd-soc-lowland-objs := lowland.o
snd-soc-littlemill-objs := littlemill.o
snd-soc-bells-objs := bells.o
snd-soc-odroid-objs := odroid.o
-snd-soc-arndale-rt5631-objs := arndale_rt5631.o
+snd-soc-arndale-objs := arndale.o
snd-soc-tm2-wm5110-objs := tm2_wm5110.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
@@ -62,5 +62,5 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
obj-$(CONFIG_SND_SOC_ODROID) += snd-soc-odroid.o
-obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
+obj-$(CONFIG_SND_SOC_ARNDALE) += snd-soc-arndale.o
obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o
diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c
new file mode 100644
index 000000000000..d64602950cbd
--- /dev/null
+++ b/sound/soc/samsung/arndale.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2014, Insignal Co., Ltd.
+//
+// Author: Claude <claude@insginal.co.kr>
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/wm8994.h"
+#include "i2s.h"
+
+static int arndale_rt5631_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int rfs, ret;
+ unsigned long rclk;
+
+ rfs = 256;
+
+ rclk = params_rate(params) * rfs;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
+ 0, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
+ 0, SND_SOC_CLOCK_OUT);
+
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops arndale_rt5631_ops = {
+ .hw_params = arndale_rt5631_hw_params,
+};
+
+static int arndale_wm1811_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int rfs, rclk;
+
+ /* Ensure AIF1CLK is >= 3 MHz for optimal performance */
+ if (params_width(params) == 24)
+ rfs = 384;
+ else if (params_rate(params) == 8000 || params_rate(params) == 11025)
+ rfs = 512;
+ else
+ rfs = 256;
+
+ rclk = params_rate(params) * rfs;
+
+ /*
+ * We add 1 to the frequency value to ensure proper EPLL setting
+ * for each audio sampling rate (see epll_24mhz_tbl in drivers/clk/
+ * samsung/clk-exynos5250.c for list of available EPLL rates).
+ * The CODEC uses clk API and the value will be rounded hence the MCLK1
+ * clock's frequency will still be exact multiple of the sample rate.
+ */
+ return snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1,
+ rclk + 1, SND_SOC_CLOCK_IN);
+}
+
+static struct snd_soc_ops arndale_wm1811_ops = {
+ .hw_params = arndale_wm1811_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(rt5631_hifi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5631-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link arndale_rt5631_dai[] = {
+ {
+ .name = "RT5631 HiFi",
+ .stream_name = "Primary",
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &arndale_rt5631_ops,
+ SND_SOC_DAILINK_REG(rt5631_hifi),
+ },
+};
+
+SND_SOC_DAILINK_DEFS(wm1811_hifi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link arndale_wm1811_dai[] = {
+ {
+ .name = "WM1811 HiFi",
+ .stream_name = "Primary",
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .ops = &arndale_wm1811_ops,
+ SND_SOC_DAILINK_REG(wm1811_hifi),
+ },
+};
+
+static struct snd_soc_card arndale_rt5631 = {
+ .name = "Arndale RT5631",
+ .owner = THIS_MODULE,
+ .dai_link = arndale_rt5631_dai,
+ .num_links = ARRAY_SIZE(arndale_rt5631_dai),
+};
+
+static struct snd_soc_card arndale_wm1811 = {
+ .name = "Arndale WM1811",
+ .owner = THIS_MODULE,
+ .dai_link = arndale_wm1811_dai,
+ .num_links = ARRAY_SIZE(arndale_wm1811_dai),
+};
+
+static void arndale_put_of_nodes(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ int i;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ of_node_put(dai_link->cpus->of_node);
+ of_node_put(dai_link->codecs->of_node);
+ }
+}
+
+static int arndale_audio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_card *card;
+ struct snd_soc_dai_link *dai_link;
+ int ret;
+
+ card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
+ card->dev = &pdev->dev;
+ dai_link = card->dai_link;
+
+ dai_link->cpus->of_node = of_parse_phandle(np, "samsung,audio-cpu", 0);
+ if (!dai_link->cpus->of_node) {
+ dev_err(&pdev->dev,
+ "Property 'samsung,audio-cpu' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ if (!dai_link->platforms->name)
+ dai_link->platforms->of_node = dai_link->cpus->of_node;
+
+ dai_link->codecs->of_node = of_parse_phandle(np, "samsung,audio-codec", 0);
+ if (!dai_link->codecs->of_node) {
+ dev_err(&pdev->dev,
+ "Property 'samsung,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err_put_of_nodes;
+ }
+
+ ret = devm_snd_soc_register_card(card->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
+ goto err_put_of_nodes;
+ }
+ return 0;
+
+err_put_of_nodes:
+ arndale_put_of_nodes(card);
+ return ret;
+}
+
+static int arndale_audio_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ arndale_put_of_nodes(card);
+ return 0;
+}
+
+static const struct of_device_id arndale_audio_of_match[] = {
+ { .compatible = "samsung,arndale-rt5631", .data = &arndale_rt5631 },
+ { .compatible = "samsung,arndale-alc5631", .data = &arndale_rt5631 },
+ { .compatible = "samsung,arndale-wm1811", .data = &arndale_wm1811 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, arndale_audio_of_match);
+
+static struct platform_driver arndale_audio_driver = {
+ .driver = {
+ .name = "arndale-audio",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = arndale_audio_of_match,
+ },
+ .probe = arndale_audio_probe,
+ .remove = arndale_audio_remove,
+};
+
+module_platform_driver(arndale_audio_driver);
+
+MODULE_AUTHOR("Claude <claude@insignal.co.kr>");
+MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c
deleted file mode 100644
index fd8c6642fb0d..000000000000
--- a/sound/soc/samsung/arndale_rt5631.c
+++ /dev/null
@@ -1,164 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-//
-// Copyright (c) 2014, Insignal Co., Ltd.
-//
-// Author: Claude <claude@insginal.co.kr>
-
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
-#include "i2s.h"
-
-static int arndale_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int rfs, ret;
- unsigned long rclk;
-
- rfs = 256;
-
- rclk = params_rate(params) * rfs;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
- 0, SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
- 0, SND_SOC_CLOCK_OUT);
-
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops arndale_ops = {
- .hw_params = arndale_hw_params,
-};
-
-SND_SOC_DAILINK_DEFS(rt5631_hifi,
- DAILINK_COMP_ARRAY(COMP_EMPTY()),
- DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5631-hifi")),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-static struct snd_soc_dai_link arndale_rt5631_dai[] = {
- {
- .name = "RT5631 HiFi",
- .stream_name = "Primary",
- .dai_fmt = SND_SOC_DAIFMT_I2S
- | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
- .ops = &arndale_ops,
- SND_SOC_DAILINK_REG(rt5631_hifi),
- },
-};
-
-static struct snd_soc_card arndale_rt5631 = {
- .name = "Arndale RT5631",
- .owner = THIS_MODULE,
- .dai_link = arndale_rt5631_dai,
- .num_links = ARRAY_SIZE(arndale_rt5631_dai),
-};
-
-static void arndale_put_of_nodes(struct snd_soc_card *card)
-{
- struct snd_soc_dai_link *dai_link;
- int i;
-
- for_each_card_prelinks(card, i, dai_link) {
- of_node_put(dai_link->cpus->of_node);
- of_node_put(dai_link->codecs->of_node);
- }
-}
-
-static int arndale_audio_probe(struct platform_device *pdev)
-{
- int n, ret;
- struct device_node *np = pdev->dev.of_node;
- struct snd_soc_card *card = &arndale_rt5631;
-
- card->dev = &pdev->dev;
-
- for (n = 0; np && n < ARRAY_SIZE(arndale_rt5631_dai); n++) {
- if (!arndale_rt5631_dai[n].cpus->dai_name) {
- arndale_rt5631_dai[n].cpus->of_node = of_parse_phandle(np,
- "samsung,audio-cpu", n);
-
- if (!arndale_rt5631_dai[n].cpus->of_node) {
- dev_err(&pdev->dev,
- "Property 'samsung,audio-cpu' missing or invalid\n");
- return -EINVAL;
- }
- }
- if (!arndale_rt5631_dai[n].platforms->name)
- arndale_rt5631_dai[n].platforms->of_node =
- arndale_rt5631_dai[n].cpus->of_node;
-
- arndale_rt5631_dai[n].codecs->name = NULL;
- arndale_rt5631_dai[n].codecs->of_node = of_parse_phandle(np,
- "samsung,audio-codec", n);
- if (!arndale_rt5631_dai[0].codecs->of_node) {
- dev_err(&pdev->dev,
- "Property 'samsung,audio-codec' missing or invalid\n");
- ret = -EINVAL;
- goto err_put_of_nodes;
- }
- }
-
- ret = devm_snd_soc_register_card(card->dev, card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
- goto err_put_of_nodes;
- }
- return 0;
-
-err_put_of_nodes:
- arndale_put_of_nodes(card);
- return ret;
-}
-
-static int arndale_audio_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- arndale_put_of_nodes(card);
- return 0;
-}
-
-static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = {
- { .compatible = "samsung,arndale-rt5631", },
- { .compatible = "samsung,arndale-alc5631", },
- {},
-};
-MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match);
-
-static struct platform_driver arndale_audio_driver = {
- .driver = {
- .name = "arndale-audio",
- .pm = &snd_soc_pm_ops,
- .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match),
- },
- .probe = arndale_audio_probe,
- .remove = arndale_audio_remove,
-};
-
-module_platform_driver(arndale_audio_driver);
-
-MODULE_AUTHOR("Claude <claude@insignal.co.kr>");
-MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c
index 65497cd477a5..294dce111b05 100644
--- a/sound/soc/samsung/idma.c
+++ b/sound/soc/samsung/idma.c
@@ -137,8 +137,9 @@ static void idma_done(void *id, int bytes_xfer)
snd_pcm_period_elapsed(substream);
}
-static int idma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int idma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd = substream->runtime->private_data;
@@ -163,14 +164,16 @@ static int idma_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int idma_hw_free(struct snd_pcm_substream *substream)
+static int idma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
-static int idma_prepare(struct snd_pcm_substream *substream)
+static int idma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct idma_ctrl *prtd = substream->runtime->private_data;
@@ -183,7 +186,8 @@ static int idma_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int idma_trigger(struct snd_pcm_substream *substream, int cmd)
+static int idma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct idma_ctrl *prtd = substream->runtime->private_data;
int ret = 0;
@@ -216,7 +220,8 @@ static int idma_trigger(struct snd_pcm_substream *substream, int cmd)
}
static snd_pcm_uframes_t
- idma_pointer(struct snd_pcm_substream *substream)
+idma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd = runtime->private_data;
@@ -233,7 +238,8 @@ static snd_pcm_uframes_t
return bytes_to_frames(substream->runtime, res);
}
-static int idma_mmap(struct snd_pcm_substream *substream,
+static int idma_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -278,7 +284,8 @@ static irqreturn_t iis_irq(int irqno, void *dev_id)
return IRQ_HANDLED;
}
-static int idma_open(struct snd_pcm_substream *substream)
+static int idma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd;
@@ -304,7 +311,8 @@ static int idma_open(struct snd_pcm_substream *substream)
return 0;
}
-static int idma_close(struct snd_pcm_substream *substream)
+static int idma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd = runtime->private_data;
@@ -319,19 +327,8 @@ static int idma_close(struct snd_pcm_substream *substream)
return 0;
}
-static const struct snd_pcm_ops idma_ops = {
- .open = idma_open,
- .close = idma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .trigger = idma_trigger,
- .pointer = idma_pointer,
- .mmap = idma_mmap,
- .hw_params = idma_hw_params,
- .hw_free = idma_hw_free,
- .prepare = idma_prepare,
-};
-
-static void idma_free(struct snd_pcm *pcm)
+static void idma_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
@@ -367,7 +364,8 @@ static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream)
return 0;
}
-static int idma_new(struct snd_soc_pcm_runtime *rtd)
+static int idma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -394,9 +392,17 @@ void idma_reg_addr_init(void __iomem *regs, dma_addr_t addr)
EXPORT_SYMBOL_GPL(idma_reg_addr_init);
static const struct snd_soc_component_driver asoc_idma_platform = {
- .ops = &idma_ops,
- .pcm_new = idma_new,
- .pcm_free = idma_free,
+ .open = idma_open,
+ .close = idma_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .trigger = idma_trigger,
+ .pointer = idma_pointer,
+ .mmap = idma_mmap,
+ .hw_params = idma_hw_params,
+ .hw_free = idma_hw_free,
+ .prepare = idma_prepare,
+ .pcm_construct = idma_new,
+ .pcm_destruct = idma_free,
};
static int asoc_idma_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
index 5aee11c94f2a..93bb80d089be 100644
--- a/sound/soc/sh/dma-sh7760.c
+++ b/sound/soc/sh/dma-sh7760.c
@@ -115,7 +115,8 @@ static void camelot_rxdma(void *data)
snd_pcm_period_elapsed(cam->rx_ss);
}
-static int camelot_pcm_open(struct snd_pcm_substream *substream)
+static int camelot_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
@@ -148,7 +149,8 @@ static int camelot_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int camelot_pcm_close(struct snd_pcm_substream *substream)
+static int camelot_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
@@ -168,7 +170,8 @@ static int camelot_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int camelot_hw_params(struct snd_pcm_substream *substream,
+static int camelot_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -191,12 +194,14 @@ static int camelot_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int camelot_hw_free(struct snd_pcm_substream *substream)
+static int camelot_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
-static int camelot_prepare(struct snd_pcm_substream *substream)
+static int camelot_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -244,7 +249,8 @@ static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
BRGREG(BRGACR) = acr | ACR_RDS;
}
-static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
+static int camelot_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
@@ -270,7 +276,8 @@ static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
-static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t camelot_pos(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -292,18 +299,8 @@ static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, pos);
}
-static const struct snd_pcm_ops camelot_pcm_ops = {
- .open = camelot_pcm_open,
- .close = camelot_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = camelot_hw_params,
- .hw_free = camelot_hw_free,
- .prepare = camelot_prepare,
- .trigger = camelot_trigger,
- .pointer = camelot_pos,
-};
-
-static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int camelot_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
@@ -319,8 +316,15 @@ static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
}
static const struct snd_soc_component_driver sh7760_soc_component = {
- .ops = &camelot_pcm_ops,
- .pcm_new = camelot_pcm_new,
+ .open = camelot_pcm_open,
+ .close = camelot_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = camelot_hw_params,
+ .hw_free = camelot_hw_free,
+ .prepare = camelot_prepare,
+ .trigger = camelot_trigger,
+ .pointer = camelot_pos,
+ .pcm_construct = camelot_pcm_new,
};
static int sh7760_soc_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 3447dbdba1f1..e384fdc8d60e 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -1718,7 +1718,8 @@ static const struct snd_pcm_hardware fsi_pcm_hardware = {
.fifo_size = 256,
};
-static int fsi_pcm_open(struct snd_pcm_substream *substream)
+static int fsi_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
@@ -1731,19 +1732,22 @@ static int fsi_pcm_open(struct snd_pcm_substream *substream)
return ret;
}
-static int fsi_hw_params(struct snd_pcm_substream *substream,
+static int fsi_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
-static int fsi_hw_free(struct snd_pcm_substream *substream)
+static int fsi_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
-static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t fsi_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
struct fsi_stream *io = fsi_stream_get(fsi, substream);
@@ -1751,14 +1755,6 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
return fsi_sample2frame(fsi, io->buff_sample_pos);
}
-static const struct snd_pcm_ops fsi_pcm_ops = {
- .open = fsi_pcm_open,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = fsi_hw_params,
- .hw_free = fsi_hw_free,
- .pointer = fsi_pointer,
-};
-
/*
* snd_soc_component
*/
@@ -1766,7 +1762,8 @@ static const struct snd_pcm_ops fsi_pcm_ops = {
#define PREALLOC_BUFFER (32 * 1024)
#define PREALLOC_BUFFER_MAX (32 * 1024)
-static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int fsi_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
snd_pcm_lib_preallocate_pages_for_all(
rtd->pcm,
@@ -1817,8 +1814,12 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = {
static const struct snd_soc_component_driver fsi_soc_component = {
.name = "fsi",
- .ops = &fsi_pcm_ops,
- .pcm_new = fsi_pcm_new,
+ .open = fsi_pcm_open,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = fsi_hw_params,
+ .hw_free = fsi_hw_free,
+ .pointer = fsi_pointer,
+ .pcm_construct = fsi_pcm_new,
};
/*
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index a6c1cf987e6e..399dc6e9bde5 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -302,7 +302,7 @@ int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io,
int rsnd_channel_normalization(int chan)
{
- if ((chan > 8) || (chan < 0))
+ if (WARN_ON((chan > 8) || (chan < 0)))
return 0;
/* TDM Extend Mode needs 8ch */
@@ -1092,7 +1092,10 @@ static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv,
j++;
}
+ of_node_put(node);
}
+
+ of_node_put(ssiu_np);
}
static void rsnd_parse_connect_simple(struct rsnd_priv *priv,
@@ -1110,11 +1113,13 @@ static void rsnd_parse_connect_graph(struct rsnd_priv *priv,
struct device_node *endpoint)
{
struct device *dev = rsnd_priv_to_dev(priv);
- struct device_node *remote_node = of_graph_get_remote_port_parent(endpoint);
+ struct device_node *remote_node;
if (!rsnd_io_to_mod_ssi(io))
return;
+ remote_node = of_graph_get_remote_port_parent(endpoint);
+
/* HDMI0 */
if (strstr(remote_node->full_name, "hdmi@fead0000")) {
rsnd_flags_set(io, RSND_STREAM_HDMI0);
@@ -1128,6 +1133,8 @@ static void rsnd_parse_connect_graph(struct rsnd_priv *priv,
}
rsnd_parse_tdm_split_mode(priv, io, endpoint);
+
+ of_node_put(remote_node);
}
void rsnd_parse_connect_common(struct rsnd_dai *rdai,
@@ -1390,8 +1397,9 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
/*
* pcm ops
*/
-static int rsnd_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+static int rsnd_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
@@ -1438,7 +1446,8 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream,
params_buffer_bytes(hw_params));
}
-static int rsnd_hw_free(struct snd_pcm_substream *substream)
+static int rsnd_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
@@ -1452,7 +1461,8 @@ static int rsnd_hw_free(struct snd_pcm_substream *substream)
return snd_pcm_lib_free_pages(substream);
}
-static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t rsnd_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
@@ -1464,13 +1474,6 @@ static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream)
return pointer;
}
-static const struct snd_pcm_ops rsnd_pcm_ops = {
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = rsnd_hw_params,
- .hw_free = rsnd_hw_free,
- .pointer = rsnd_pointer,
-};
-
/*
* snd_kcontrol
*/
@@ -1664,8 +1667,11 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
* snd_soc_component
*/
static const struct snd_soc_component_driver rsnd_soc_component = {
- .ops = &rsnd_pcm_ops,
.name = "rsnd",
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = rsnd_hw_params,
+ .hw_free = rsnd_hw_free,
+ .pointer = rsnd_pointer,
};
static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 28f65eba2bb4..95aa26d62e4f 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -165,14 +165,40 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_async_tx_descriptor *desc;
struct dma_slave_config cfg = {};
+ enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
int is_play = rsnd_io_is_play(io);
int ret;
+ /*
+ * in case of monaural data writing or reading through Audio-DMAC
+ * data is always in Left Justified format, so both src and dst
+ * DMA Bus width need to be set equal to physical data width.
+ */
+ if (rsnd_runtime_channel_original(io) == 1) {
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ int bits = snd_pcm_format_physical_width(runtime->format);
+
+ switch (bits) {
+ case 8:
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ break;
+ case 16:
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case 32:
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ default:
+ dev_err(dev, "invalid format width %d\n", bits);
+ return -EINVAL;
+ }
+ }
+
cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
cfg.src_addr = dma->src_addr;
cfg.dst_addr = dma->dst_addr;
- cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.src_addr_width = buswidth;
+ cfg.dst_addr_width = buswidth;
dev_dbg(dev, "%s %pad -> %pad\n",
rsnd_mod_name(mod),
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
index 78c3145b4109..a5e21e554da2 100644
--- a/sound/soc/sh/siu_pcm.c
+++ b/sound/soc/sh/siu_pcm.c
@@ -281,7 +281,8 @@ static int siu_pcm_stmread_stop(struct siu_port *port_info)
return 0;
}
-static int siu_pcm_hw_params(struct snd_pcm_substream *ss,
+static int siu_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss,
struct snd_pcm_hw_params *hw_params)
{
struct siu_info *info = siu_i2s_data;
@@ -297,7 +298,8 @@ static int siu_pcm_hw_params(struct snd_pcm_substream *ss,
return ret;
}
-static int siu_pcm_hw_free(struct snd_pcm_substream *ss)
+static int siu_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct siu_info *info = siu_i2s_data;
struct siu_port *port_info = siu_port_info(ss);
@@ -324,11 +326,10 @@ static bool filter(struct dma_chan *chan, void *slave)
return true;
}
-static int siu_pcm_open(struct snd_pcm_substream *ss)
+static int siu_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
/* Playback / Capture */
- struct snd_soc_pcm_runtime *rtd = ss->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct siu_platform *pdata = component->dev->platform_data;
struct siu_info *info = siu_i2s_data;
struct siu_port *port_info = siu_port_info(ss);
@@ -367,7 +368,8 @@ static int siu_pcm_open(struct snd_pcm_substream *ss)
return 0;
}
-static int siu_pcm_close(struct snd_pcm_substream *ss)
+static int siu_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct siu_info *info = siu_i2s_data;
struct device *dev = ss->pcm->card->dev;
@@ -389,7 +391,8 @@ static int siu_pcm_close(struct snd_pcm_substream *ss)
return 0;
}
-static int siu_pcm_prepare(struct snd_pcm_substream *ss)
+static int siu_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct siu_info *info = siu_i2s_data;
struct siu_port *port_info = siu_port_info(ss);
@@ -435,7 +438,8 @@ static int siu_pcm_prepare(struct snd_pcm_substream *ss)
return 0;
}
-static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
+static int siu_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss, int cmd)
{
struct siu_info *info = siu_i2s_data;
struct device *dev = ss->pcm->card->dev;
@@ -477,7 +481,9 @@ static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
* So far only resolution of one period is supported, subject to extending the
* dmangine API
*/
-static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
+static snd_pcm_uframes_t
+siu_pcm_pointer_dma(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct device *dev = ss->pcm->card->dev;
struct siu_info *info = siu_i2s_data;
@@ -512,7 +518,8 @@ static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, ptr);
}
-static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int siu_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
/* card->dev == socdev->dev, see snd_soc_new_pcms() */
struct snd_card *card = rtd->card->snd_card;
@@ -558,7 +565,8 @@ static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static void siu_pcm_free(struct snd_pcm *pcm)
+static void siu_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct platform_device *pdev = to_platform_device(pcm->card->dev);
struct siu_port *port_info = siu_ports[pdev->id];
@@ -571,21 +579,17 @@ static void siu_pcm_free(struct snd_pcm *pcm)
dev_dbg(pcm->card->dev, "%s\n", __func__);
}
-static const struct snd_pcm_ops siu_pcm_ops = {
+struct const snd_soc_component_driver siu_component = {
+ .name = DRV_NAME,
.open = siu_pcm_open,
.close = siu_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
+ .ioctl = snd_soc_pcm_lib_ioctl,
.hw_params = siu_pcm_hw_params,
.hw_free = siu_pcm_hw_free,
.prepare = siu_pcm_prepare,
.trigger = siu_pcm_trigger,
.pointer = siu_pcm_pointer_dma,
-};
-
-struct snd_soc_component_driver siu_component = {
- .name = DRV_NAME,
- .ops = &siu_pcm_ops,
- .pcm_new = siu_pcm_new,
- .pcm_free = siu_pcm_free,
+ .pcm_construct = siu_pcm_new,
+ .pcm_destruct = siu_pcm_free,
};
EXPORT_SYMBOL_GPL(siu_component);
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index 79ffc2820ba9..1590e805d016 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -314,30 +314,24 @@ void snd_soc_component_module_put(struct snd_soc_component *component,
int snd_soc_component_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- if (component->driver->ops &&
- component->driver->ops->open)
- return component->driver->ops->open(substream);
-
+ if (component->driver->open)
+ return component->driver->open(component, substream);
return 0;
}
int snd_soc_component_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- if (component->driver->ops &&
- component->driver->ops->close)
- return component->driver->ops->close(substream);
-
+ if (component->driver->close)
+ return component->driver->close(component, substream);
return 0;
}
int snd_soc_component_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- if (component->driver->ops &&
- component->driver->ops->prepare)
- return component->driver->ops->prepare(substream);
-
+ if (component->driver->prepare)
+ return component->driver->prepare(component, substream);
return 0;
}
@@ -345,20 +339,17 @@ int snd_soc_component_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- if (component->driver->ops &&
- component->driver->ops->hw_params)
- return component->driver->ops->hw_params(substream, params);
-
+ if (component->driver->hw_params)
+ return component->driver->hw_params(component,
+ substream, params);
return 0;
}
int snd_soc_component_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- if (component->driver->ops &&
- component->driver->ops->hw_free)
- return component->driver->ops->hw_free(substream);
-
+ if (component->driver->hw_free)
+ return component->driver->hw_free(component, substream);
return 0;
}
@@ -366,10 +357,8 @@ int snd_soc_component_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
int cmd)
{
- if (component->driver->ops &&
- component->driver->ops->trigger)
- return component->driver->ops->trigger(substream, cmd);
-
+ if (component->driver->trigger)
+ return component->driver->trigger(component, substream, cmd);
return 0;
}
@@ -431,14 +420,10 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream)
struct snd_soc_component *component;
struct snd_soc_rtdcom_list *rtdcom;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- /* FIXME: use 1st pointer */
- if (component->driver->ops &&
- component->driver->ops->pointer)
- return component->driver->ops->pointer(substream);
- }
+ /* FIXME: use 1st pointer */
+ for_each_rtd_components(rtd, rtdcom, component)
+ if (component->driver->pointer)
+ return component->driver->pointer(component, substream);
return 0;
}
@@ -450,15 +435,11 @@ int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream,
struct snd_soc_component *component;
struct snd_soc_rtdcom_list *rtdcom;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- /* FIXME: use 1st ioctl */
- if (component->driver->ops &&
- component->driver->ops->ioctl)
- return component->driver->ops->ioctl(substream,
- cmd, arg);
- }
+ /* FIXME: use 1st ioctl */
+ for_each_rtd_components(rtd, rtdcom, component)
+ if (component->driver->ioctl)
+ return component->driver->ioctl(component, substream,
+ cmd, arg);
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
@@ -471,15 +452,11 @@ int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream,
struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_component *component;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- /* FIXME. it returns 1st copy now */
- if (component->driver->ops &&
- component->driver->ops->copy_user)
- return component->driver->ops->copy_user(
- substream, channel, pos, buf, bytes);
- }
+ /* FIXME. it returns 1st copy now */
+ for_each_rtd_components(rtd, rtdcom, component)
+ if (component->driver->copy_user)
+ return component->driver->copy_user(
+ component, substream, channel, pos, buf, bytes);
return -EINVAL;
}
@@ -492,13 +469,11 @@ struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream,
struct snd_soc_component *component;
struct page *page;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- /* FIXME. it returns 1st page now */
- if (component->driver->ops &&
- component->driver->ops->page) {
- page = component->driver->ops->page(substream, offset);
+ /* FIXME. it returns 1st page now */
+ for_each_rtd_components(rtd, rtdcom, component) {
+ if (component->driver->page) {
+ page = component->driver->page(component,
+ substream, offset);
if (page)
return page;
}
@@ -514,30 +489,24 @@ int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_component *component;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- /* FIXME. it returns 1st mmap now */
- if (component->driver->ops &&
- component->driver->ops->mmap)
- return component->driver->ops->mmap(substream, vma);
- }
+ /* FIXME. it returns 1st mmap now */
+ for_each_rtd_components(rtd, rtdcom, component)
+ if (component->driver->mmap)
+ return component->driver->mmap(component,
+ substream, vma);
return -EINVAL;
}
-int snd_soc_pcm_component_new(struct snd_pcm *pcm)
+int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_pcm_runtime *rtd = pcm->private_data;
struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_component *component;
int ret;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- if (component->driver->pcm_new) {
- ret = component->driver->pcm_new(rtd);
+ for_each_rtd_components(rtd, rtdcom, component) {
+ if (component->driver->pcm_construct) {
+ ret = component->driver->pcm_construct(component, rtd);
if (ret < 0)
return ret;
}
@@ -546,16 +515,12 @@ int snd_soc_pcm_component_new(struct snd_pcm *pcm)
return 0;
}
-void snd_soc_pcm_component_free(struct snd_pcm *pcm)
+void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_pcm_runtime *rtd = pcm->private_data;
struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_component *component;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- if (component->driver->pcm_free)
- component->driver->pcm_free(pcm);
- }
+ for_each_rtd_components(rtd, rtdcom, component)
+ if (component->driver->pcm_destruct)
+ component->driver->pcm_destruct(component, rtd->pcm);
}
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 9e54d8ae6d2c..61f230324164 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -28,9 +28,7 @@ static int soc_compr_components_open(struct snd_compr_stream *cstream,
struct snd_soc_rtdcom_list *rtdcom;
int ret;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->open)
continue;
@@ -57,9 +55,7 @@ static int soc_compr_components_free(struct snd_compr_stream *cstream,
struct snd_soc_component *component;
struct snd_soc_rtdcom_list *rtdcom;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (component == last)
break;
@@ -353,9 +349,7 @@ static int soc_compr_components_trigger(struct snd_compr_stream *cstream,
struct snd_soc_rtdcom_list *rtdcom;
int ret;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->trigger)
continue;
@@ -458,9 +452,7 @@ static int soc_compr_components_set_params(struct snd_compr_stream *cstream,
struct snd_soc_rtdcom_list *rtdcom;
int ret;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->set_params)
continue;
@@ -601,9 +593,7 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream,
goto err;
}
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->get_params)
continue;
@@ -627,9 +617,7 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream,
mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->get_caps)
continue;
@@ -652,9 +640,7 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream,
mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->get_codec_caps)
continue;
@@ -684,9 +670,7 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
goto err;
}
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->ack)
continue;
@@ -715,9 +699,7 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream,
if (cpu_dai->driver->cops && cpu_dai->driver->cops->pointer)
cpu_dai->driver->cops->pointer(cstream, tstamp, cpu_dai);
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->pointer)
continue;
@@ -740,9 +722,7 @@ static int soc_compr_copy(struct snd_compr_stream *cstream,
mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->copy)
continue;
@@ -770,9 +750,7 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream,
return ret;
}
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->set_metadata)
continue;
@@ -801,9 +779,7 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream,
return ret;
}
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->get_metadata)
continue;
@@ -932,9 +908,7 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops));
}
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->compr_ops ||
!component->driver->compr_ops->copy)
continue;
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 88978a3036c4..062653ab03a3 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -125,6 +125,9 @@ static umode_t soc_dev_attr_is_visible(struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
+ if (!rtd)
+ return 0;
+
if (attr == &dev_attr_pmdown_time.attr)
return attr->mode; /* always visible */
return rtd->num_codecs ? attr->mode : 0; /* enabled only with codec */
@@ -274,43 +277,58 @@ static inline void snd_soc_debugfs_exit(void)
#endif
+/*
+ * This is glue code between snd_pcm_lib_ioctl() and
+ * snd_soc_component_driver :: ioctl
+ */
+int snd_soc_pcm_lib_ioctl(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+EXPORT_SYMBOL_GPL(snd_soc_pcm_lib_ioctl);
+
static int snd_soc_rtdcom_add(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_component *component)
{
struct snd_soc_rtdcom_list *rtdcom;
+ struct snd_soc_component *comp;
- for_each_rtdcom(rtd, rtdcom) {
+ for_each_rtd_components(rtd, rtdcom, comp) {
/* already connected */
- if (rtdcom->component == component)
+ if (comp == component)
return 0;
}
- rtdcom = kmalloc(sizeof(*rtdcom), GFP_KERNEL);
+ /*
+ * created rtdcom here will be freed when rtd->dev was freed.
+ * see
+ * soc_free_pcm_runtime() :: device_unregister(rtd->dev)
+ */
+ rtdcom = devm_kzalloc(rtd->dev, sizeof(*rtdcom), GFP_KERNEL);
if (!rtdcom)
return -ENOMEM;
rtdcom->component = component;
INIT_LIST_HEAD(&rtdcom->list);
+ /*
+ * When rtd was freed, created rtdcom here will be
+ * also freed.
+ * And we don't need to call list_del(&rtdcom->list)
+ * when freed, because rtd is also freed.
+ */
list_add_tail(&rtdcom->list, &rtd->component_list);
return 0;
}
-static void snd_soc_rtdcom_del_all(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_rtdcom_list *rtdcom1, *rtdcom2;
-
- for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2)
- kfree(rtdcom1);
-
- INIT_LIST_HEAD(&rtd->component_list);
-}
-
struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
const char *driver_name)
{
struct snd_soc_rtdcom_list *rtdcom;
+ struct snd_soc_component *component;
if (!driver_name)
return NULL;
@@ -323,8 +341,8 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
* But, if many components which have same driver name are connected
* to 1 rtd, this function will return 1st found component.
*/
- for_each_rtdcom(rtd, rtdcom) {
- const char *component_name = rtdcom->component->driver->name;
+ for_each_rtd_components(rtd, rtdcom, component) {
+ const char *component_name = component->driver->name;
if (!component_name)
continue;
@@ -338,6 +356,39 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
}
EXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup);
+static struct snd_soc_component
+*snd_soc_lookup_component_nolocked(struct device *dev, const char *driver_name)
+{
+ struct snd_soc_component *component;
+ struct snd_soc_component *found_component;
+
+ found_component = NULL;
+ for_each_component(component) {
+ if ((dev == component->dev) &&
+ (!driver_name ||
+ (driver_name == component->driver->name) ||
+ (strcmp(component->driver->name, driver_name) == 0))) {
+ found_component = component;
+ break;
+ }
+ }
+
+ return found_component;
+}
+
+struct snd_soc_component *snd_soc_lookup_component(struct device *dev,
+ const char *driver_name)
+{
+ struct snd_soc_component *component;
+
+ mutex_lock(&client_mutex);
+ component = snd_soc_lookup_component_nolocked(dev, driver_name);
+ mutex_unlock(&client_mutex);
+
+ return component;
+}
+EXPORT_SYMBOL_GPL(snd_soc_lookup_component);
+
struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
const char *dai_link, int stream)
{
@@ -355,58 +406,104 @@ EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
static const struct snd_soc_ops null_snd_soc_ops;
+static void soc_release_rtd_dev(struct device *dev)
+{
+ /* "dev" means "rtd->dev" */
+ kfree(dev);
+}
+
+static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
+{
+ if (!rtd)
+ return;
+
+ list_del(&rtd->list);
+
+ flush_delayed_work(&rtd->delayed_work);
+ snd_soc_pcm_component_free(rtd);
+
+ /*
+ * we don't need to call kfree() for rtd->dev
+ * see
+ * soc_release_rtd_dev()
+ *
+ * We don't need rtd->dev NULL check, because
+ * it is alloced *before* rtd.
+ * see
+ * soc_new_pcm_runtime()
+ */
+ device_unregister(rtd->dev);
+}
+
static struct snd_soc_pcm_runtime *soc_new_pcm_runtime(
struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
{
struct snd_soc_pcm_runtime *rtd;
+ struct device *dev;
+ int ret;
- rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
- if (!rtd)
+ /*
+ * for rtd->dev
+ */
+ dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!dev)
return NULL;
- INIT_LIST_HEAD(&rtd->component_list);
- rtd->card = card;
- rtd->dai_link = dai_link;
- if (!rtd->dai_link->ops)
- rtd->dai_link->ops = &null_snd_soc_ops;
+ dev->parent = card->dev;
+ dev->release = soc_release_rtd_dev;
+ dev->groups = soc_dev_attr_groups;
- rtd->codec_dais = kcalloc(dai_link->num_codecs,
- sizeof(struct snd_soc_dai *),
- GFP_KERNEL);
- if (!rtd->codec_dais) {
- kfree(rtd);
+ dev_set_name(dev, "%s", dai_link->name);
+
+ ret = device_register(dev);
+ if (ret < 0) {
+ put_device(dev); /* soc_release_rtd_dev */
return NULL;
}
- return rtd;
-}
+ /*
+ * for rtd
+ */
+ rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL);
+ if (!rtd)
+ goto free_rtd;
-static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
-{
- kfree(rtd->codec_dais);
- snd_soc_rtdcom_del_all(rtd);
- kfree(rtd);
-}
+ rtd->dev = dev;
+ dev_set_drvdata(dev, rtd);
+
+ /*
+ * for rtd->codec_dais
+ */
+ rtd->codec_dais = devm_kcalloc(dev, dai_link->num_codecs,
+ sizeof(struct snd_soc_dai *),
+ GFP_KERNEL);
+ if (!rtd->codec_dais)
+ goto free_rtd;
+
+ /*
+ * rtd remaining settings
+ */
+ INIT_LIST_HEAD(&rtd->component_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);
+
+ rtd->card = card;
+ rtd->dai_link = dai_link;
+ if (!rtd->dai_link->ops)
+ rtd->dai_link->ops = &null_snd_soc_ops;
-static void soc_add_pcm_runtime(struct snd_soc_card *card,
- struct snd_soc_pcm_runtime *rtd)
-{
/* see for_each_card_rtds */
list_add_tail(&rtd->list, &card->rtd_list);
rtd->num = card->num_rtd;
card->num_rtd++;
-}
-static void soc_remove_pcm_runtimes(struct snd_soc_card *card)
-{
- struct snd_soc_pcm_runtime *rtd, *_rtd;
-
- for_each_card_rtds_safe(card, rtd, _rtd) {
- list_del(&rtd->list);
- soc_free_pcm_runtime(rtd);
- }
+ return rtd;
- card->num_rtd = 0;
+free_rtd:
+ soc_free_pcm_runtime(rtd);
+ return NULL;
}
struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
@@ -859,37 +956,168 @@ struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_find_dai_link);
-static bool soc_is_dai_link_bound(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
+static int soc_dai_link_sanity_check(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link)
{
- struct snd_soc_pcm_runtime *rtd;
+ int i;
+ struct snd_soc_dai_link_component *codec, *platform;
- for_each_card_rtds(card, rtd) {
- if (rtd->dai_link == dai_link)
- return true;
+ for_each_link_codecs(link, i, codec) {
+ /*
+ * Codec must be specified by 1 of name or OF node,
+ * not both or neither.
+ */
+ if (!!codec->name == !!codec->of_node) {
+ dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+
+ /* Codec DAI name must be specified */
+ if (!codec->dai_name) {
+ dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+
+ /*
+ * Defer card registration if codec component is not added to
+ * component list.
+ */
+ if (!soc_find_component(codec))
+ return -EPROBE_DEFER;
}
- return false;
+ for_each_link_platforms(link, i, platform) {
+ /*
+ * Platform may be specified by either name or OF node, but it
+ * can be left unspecified, then no components will be inserted
+ * in the rtdcom list
+ */
+ if (!!platform->name == !!platform->of_node) {
+ dev_err(card->dev,
+ "ASoC: Neither/both platform name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+
+ /*
+ * Defer card registration if platform component is not added to
+ * component list.
+ */
+ if (!soc_find_component(platform))
+ 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;
+ }
+
+ /*
+ * 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;
+
+ /*
+ * 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;
+ }
+
+ return 0;
+}
+
+/**
+ * snd_soc_remove_dai_link - Remove a DAI link from the list
+ * @card: The ASoC card that owns the link
+ * @dai_link: The DAI link to remove
+ *
+ * This function removes a DAI link from the ASoC card's link list.
+ *
+ * For DAI links previously added by topology, topology should
+ * remove them by using the dobj embedded in the link.
+ */
+void snd_soc_remove_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ struct snd_soc_pcm_runtime *rtd;
+
+ lockdep_assert_held(&client_mutex);
+
+ /*
+ * Notify the machine driver for extra destruction
+ */
+ if (card->remove_dai_link)
+ card->remove_dai_link(card, dai_link);
+
+ list_del(&dai_link->list);
+
+ rtd = snd_soc_get_pcm_runtime(card, dai_link->name);
+ if (rtd)
+ soc_free_pcm_runtime(rtd);
}
+EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
-static int soc_bind_dai_link(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
+/**
+ * snd_soc_add_dai_link - Add a DAI link dynamically
+ * @card: The ASoC card to which the DAI link is added
+ * @dai_link: The new DAI link to add
+ *
+ * This function adds a DAI link to the ASoC card's link list.
+ *
+ * Note: Topology can use this API to add DAI links when probing the
+ * topology component. And machine drivers can still define static
+ * DAI links in dai_link array.
+ */
+int snd_soc_add_dai_link(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_component *component;
- int i;
+ int i, ret;
+
+ lockdep_assert_held(&client_mutex);
+
+ /*
+ * Notify the machine driver for extra initialization
+ */
+ if (card->add_dai_link)
+ card->add_dai_link(card, dai_link);
if (dai_link->ignore)
return 0;
dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
- if (soc_is_dai_link_bound(card, dai_link)) {
- dev_dbg(card->dev, "ASoC: dai link %s already bound\n",
- dai_link->name);
- return 0;
- }
+ ret = soc_dai_link_sanity_check(card, dai_link);
+ if (ret < 0)
+ return ret;
rtd = soc_new_pcm_runtime(card, dai_link);
if (!rtd)
@@ -930,13 +1158,16 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
}
}
- soc_add_pcm_runtime(card, rtd);
+ /* see for_each_card_links */
+ list_add_tail(&dai_link->list, &card->dai_link_list);
+
return 0;
_err_defer:
soc_free_pcm_runtime(rtd);
return -EPROBE_DEFER;
}
+EXPORT_SYMBOL_GPL(snd_soc_add_dai_link);
static void soc_set_of_name_prefix(struct snd_soc_component *component)
{
@@ -973,8 +1204,16 @@ static void soc_set_name_prefix(struct snd_soc_card *card,
soc_set_of_name_prefix(component);
}
-static void soc_cleanup_component(struct snd_soc_component *component)
+static void soc_remove_component(struct snd_soc_component *component,
+ int probed)
{
+
+ if (!component->card)
+ return;
+
+ if (probed)
+ snd_soc_component_remove(component);
+
/* For framework level robustness */
snd_soc_component_set_jack(component, NULL, NULL);
@@ -985,22 +1224,13 @@ static void soc_cleanup_component(struct snd_soc_component *component)
snd_soc_component_module_put_when_remove(component);
}
-static void soc_remove_component(struct snd_soc_component *component)
-{
- if (!component->card)
- return;
-
- snd_soc_component_remove(component);
-
- soc_cleanup_component(component);
-}
-
static int soc_probe_component(struct snd_soc_card *card,
struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(component);
struct snd_soc_dai *dai;
+ int probed = 0;
int ret;
if (!strcmp(component->name, "snd-soc-dummy"))
@@ -1056,6 +1286,7 @@ static int soc_probe_component(struct snd_soc_card *card,
dapm->bias_level != SND_SOC_BIAS_OFF,
"codec %s can not start from non-off bias with idle_bias_off==1\n",
component->name);
+ probed = 1;
/* machine specific init */
if (component->init) {
@@ -1084,7 +1315,7 @@ static int soc_probe_component(struct snd_soc_card *card,
err_probe:
if (ret < 0)
- soc_cleanup_component(component);
+ soc_remove_component(component, probed);
return ret;
}
@@ -1126,7 +1357,6 @@ static int soc_probe_dai(struct snd_soc_dai *dai, int order)
return 0;
}
-static void soc_rtd_free(struct snd_soc_pcm_runtime *rtd); /* remove me */
static void soc_remove_link_dais(struct snd_soc_card *card)
{
int i;
@@ -1136,10 +1366,6 @@ static void soc_remove_link_dais(struct snd_soc_card *card)
for_each_comp_order(order) {
for_each_card_rtds(card, rtd) {
-
- /* finalize rtd device */
- soc_rtd_free(rtd);
-
/* remove the CODEC DAI */
for_each_rtd_codec_dai(rtd, i, codec_dai)
soc_remove_dai(codec_dai, order);
@@ -1187,13 +1413,11 @@ static void soc_remove_link_components(struct snd_soc_card *card)
for_each_comp_order(order) {
for_each_card_rtds(card, rtd) {
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (component->driver->remove_order != order)
continue;
- soc_remove_component(component);
+ soc_remove_component(component, 1);
}
}
}
@@ -1208,9 +1432,7 @@ static int soc_probe_link_components(struct snd_soc_card *card)
for_each_comp_order(order) {
for_each_card_rtds(card, rtd) {
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (component->driver->probe_order != order)
continue;
@@ -1224,119 +1446,6 @@ static int soc_probe_link_components(struct snd_soc_card *card)
return 0;
}
-static void soc_remove_dai_links(struct snd_soc_card *card)
-{
- struct snd_soc_dai_link *link, *_link;
-
- soc_remove_link_dais(card);
-
- soc_remove_link_components(card);
-
- for_each_card_links_safe(card, link, _link) {
- if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK)
- dev_warn(card->dev, "Topology forgot to remove link %s?\n",
- link->name);
-
- list_del(&link->list);
- }
-}
-
-static int soc_init_dai_link(struct snd_soc_card *card,
- struct snd_soc_dai_link *link)
-{
- int i;
- struct snd_soc_dai_link_component *codec, *platform;
-
- for_each_link_codecs(link, i, codec) {
- /*
- * Codec must be specified by 1 of name or OF node,
- * not both or neither.
- */
- if (!!codec->name == !!codec->of_node) {
- dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
-
- /* Codec DAI name must be specified */
- if (!codec->dai_name) {
- dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
- link->name);
- return -EINVAL;
- }
-
- /*
- * Defer card registration if codec component is not added to
- * component list.
- */
- if (!soc_find_component(codec))
- return -EPROBE_DEFER;
- }
-
- for_each_link_platforms(link, i, platform) {
- /*
- * Platform may be specified by either name or OF node, but it
- * can be left unspecified, then no components will be inserted
- * in the rtdcom list
- */
- if (!!platform->name == !!platform->of_node) {
- dev_err(card->dev,
- "ASoC: Neither/both platform name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
-
- /*
- * Defer card registration if platform component is not added to
- * component list.
- */
- if (!soc_find_component(platform))
- 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;
- }
-
- /*
- * Defer card registartion 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;
-
- /*
- * 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;
- }
-
- return 0;
-}
-
void snd_soc_disconnect_sync(struct device *dev)
{
struct snd_soc_component *component =
@@ -1349,117 +1458,6 @@ void snd_soc_disconnect_sync(struct device *dev)
}
EXPORT_SYMBOL_GPL(snd_soc_disconnect_sync);
-/**
- * snd_soc_add_dai_link - Add a DAI link dynamically
- * @card: The ASoC card to which the DAI link is added
- * @dai_link: The new DAI link to add
- *
- * This function adds a DAI link to the ASoC card's link list.
- *
- * Note: Topology can use this API to add DAI links when probing the
- * topology component. And machine drivers can still define static
- * DAI links in dai_link array.
- */
-int snd_soc_add_dai_link(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
-{
- if (dai_link->dobj.type
- && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
- dev_err(card->dev, "Invalid dai link type %d\n",
- dai_link->dobj.type);
- return -EINVAL;
- }
-
- lockdep_assert_held(&client_mutex);
- /*
- * Notify the machine driver for extra initialization
- * on the link created by topology.
- */
- if (dai_link->dobj.type && card->add_dai_link)
- card->add_dai_link(card, dai_link);
-
- /* see for_each_card_links */
- list_add_tail(&dai_link->list, &card->dai_link_list);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_add_dai_link);
-
-/**
- * snd_soc_remove_dai_link - Remove a DAI link from the list
- * @card: The ASoC card that owns the link
- * @dai_link: The DAI link to remove
- *
- * This function removes a DAI link from the ASoC card's link list.
- *
- * For DAI links previously added by topology, topology should
- * remove them by using the dobj embedded in the link.
- */
-void snd_soc_remove_dai_link(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
-{
- if (dai_link->dobj.type
- && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
- dev_err(card->dev, "Invalid dai link type %d\n",
- dai_link->dobj.type);
- return;
- }
-
- lockdep_assert_held(&client_mutex);
- /*
- * Notify the machine driver for extra destruction
- * on the link created by topology.
- */
- if (dai_link->dobj.type && card->remove_dai_link)
- card->remove_dai_link(card, dai_link);
-
- list_del(&dai_link->list);
-}
-EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
-
-static void soc_rtd_free(struct snd_soc_pcm_runtime *rtd)
-{
- if (rtd->dev_registered) {
- /* we don't need to call kfree() for rtd->dev */
- device_unregister(rtd->dev);
- rtd->dev_registered = 0;
- }
-}
-
-static void soc_rtd_release(struct device *dev)
-{
- kfree(dev);
-}
-
-static int soc_rtd_init(struct snd_soc_pcm_runtime *rtd, const char *name)
-{
- int ret = 0;
-
- /* register the rtd device */
- rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
- if (!rtd->dev)
- return -ENOMEM;
- rtd->dev->parent = rtd->card->dev;
- rtd->dev->release = soc_rtd_release;
- rtd->dev->groups = soc_dev_attr_groups;
- dev_set_name(rtd->dev, "%s", name);
- dev_set_drvdata(rtd->dev, rtd);
- 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);
- ret = device_register(rtd->dev);
- if (ret < 0) {
- /* calling put_device() here to free the rtd->dev */
- put_device(rtd->dev);
- dev_err(rtd->card->dev,
- "ASoC: failed to register runtime device: %d\n", ret);
- return ret;
- }
- rtd->dev_registered = 1;
- return 0;
-}
-
static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais,
struct snd_soc_pcm_runtime *rtd)
{
@@ -1509,10 +1507,6 @@ static int soc_link_init(struct snd_soc_card *card,
return ret;
}
- ret = soc_rtd_init(rtd, dai_link->name);
- if (ret)
- return ret;
-
/* add DPCM sysfs entries */
soc_dpcm_debugfs_add(rtd);
@@ -1523,9 +1517,7 @@ static int soc_link_init(struct snd_soc_card *card,
* topology based drivers can use the DAI link id field to set PCM
* device number and then use rtd + a base offset of the BEs.
*/
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (!component->driver->use_dai_pcm_id)
continue;
@@ -1590,21 +1582,18 @@ static int soc_bind_aux_dev(struct snd_soc_card *card)
static int soc_probe_aux_devices(struct snd_soc_card *card)
{
- struct snd_soc_component *comp;
+ struct snd_soc_component *component;
int order;
int ret;
for_each_comp_order(order) {
- for_each_card_auxs(card, comp) {
- if (comp->driver->probe_order == order) {
- ret = soc_probe_component(card, comp);
- if (ret < 0) {
- dev_err(card->dev,
- "ASoC: failed to probe aux component %s %d\n",
- comp->name, ret);
- return ret;
- }
- }
+ for_each_card_auxs(card, component) {
+ if (component->driver->probe_order != order)
+ continue;
+
+ ret = soc_probe_component(card, component);
+ if (ret < 0)
+ return ret;
}
}
@@ -1619,7 +1608,7 @@ static void soc_remove_aux_devices(struct snd_soc_card *card)
for_each_comp_order(order) {
for_each_card_auxs_safe(card, comp, _comp) {
if (comp->driver->remove_order == order)
- soc_remove_component(comp);
+ soc_remove_component(comp, 1);
}
}
}
@@ -1729,6 +1718,23 @@ static int is_dmi_valid(const char *field)
return 1;
}
+/*
+ * Append a string to card->dmi_longname with character cleanups.
+ */
+static void append_dmi_string(struct snd_soc_card *card, const char *str)
+{
+ char *dst = card->dmi_longname;
+ size_t dst_len = sizeof(card->dmi_longname);
+ size_t len;
+
+ len = strlen(dst);
+ snprintf(dst + len, dst_len - len, "-%s", str);
+
+ len++; /* skip the separator "-" */
+ if (len < dst_len)
+ cleanup_dmi_name(dst + len);
+}
+
/**
* snd_soc_set_dmi_name() - Register DMI names to card
* @card: The card to register DMI names
@@ -1763,61 +1769,37 @@ static int is_dmi_valid(const char *field)
int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
{
const char *vendor, *product, *product_version, *board;
- size_t longname_buf_size = sizeof(card->snd_card->longname);
- size_t len;
if (card->long_name)
return 0; /* long name already set by driver or from DMI */
- /* make up dmi long name as: vendor.product.version.board */
+ /* make up dmi long name as: vendor-product-version-board */
vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
if (!vendor || !is_dmi_valid(vendor)) {
dev_warn(card->dev, "ASoC: no DMI vendor name!\n");
return 0;
}
- snprintf(card->dmi_longname, sizeof(card->snd_card->longname),
- "%s", vendor);
+ snprintf(card->dmi_longname, sizeof(card->dmi_longname), "%s", vendor);
cleanup_dmi_name(card->dmi_longname);
product = dmi_get_system_info(DMI_PRODUCT_NAME);
if (product && is_dmi_valid(product)) {
- len = strlen(card->dmi_longname);
- snprintf(card->dmi_longname + len,
- longname_buf_size - len,
- "-%s", product);
-
- len++; /* skip the separator "-" */
- if (len < longname_buf_size)
- cleanup_dmi_name(card->dmi_longname + len);
+ append_dmi_string(card, product);
/*
* some vendors like Lenovo may only put a self-explanatory
* name in the product version field
*/
product_version = dmi_get_system_info(DMI_PRODUCT_VERSION);
- if (product_version && is_dmi_valid(product_version)) {
- len = strlen(card->dmi_longname);
- snprintf(card->dmi_longname + len,
- longname_buf_size - len,
- "-%s", product_version);
-
- len++;
- if (len < longname_buf_size)
- cleanup_dmi_name(card->dmi_longname + len);
- }
+ if (product_version && is_dmi_valid(product_version))
+ append_dmi_string(card, product_version);
}
board = dmi_get_system_info(DMI_BOARD_NAME);
if (board && is_dmi_valid(board)) {
- len = strlen(card->dmi_longname);
- snprintf(card->dmi_longname + len,
- longname_buf_size - len,
- "-%s", board);
-
- len++;
- if (len < longname_buf_size)
- cleanup_dmi_name(card->dmi_longname + len);
+ if (!product || strcasecmp(board, product))
+ append_dmi_string(card, board);
} else if (!product) {
/* fall back to using legacy name */
dev_warn(card->dev, "ASoC: no DMI board/product name!\n");
@@ -1825,16 +1807,8 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
}
/* Add flavour to dmi long name */
- if (flavour) {
- len = strlen(card->dmi_longname);
- snprintf(card->dmi_longname + len,
- longname_buf_size - len,
- "-%s", flavour);
-
- len++;
- if (len < longname_buf_size)
- cleanup_dmi_name(card->dmi_longname + len);
- }
+ if (flavour)
+ append_dmi_string(card, flavour);
/* set the card long name */
card->long_name = card->dmi_longname;
@@ -1853,7 +1827,7 @@ static void soc_check_tplg_fes(struct snd_soc_card *card)
for_each_component(component) {
- /* does this component override FEs ? */
+ /* does this component override BEs ? */
if (!component->driver->ignore_machine)
continue;
@@ -1874,7 +1848,7 @@ match:
continue;
}
- dev_info(card->dev, "info: override FE DAI link %s\n",
+ dev_info(card->dev, "info: override BE DAI link %s\n",
card->dai_link[i].name);
/* override platform component */
@@ -1918,17 +1892,58 @@ match:
}
}
-static void soc_cleanup_card_resources(struct snd_soc_card *card)
+#define soc_setup_card_name(name, name1, name2, norm) \
+ __soc_setup_card_name(name, sizeof(name), name1, name2, norm)
+static void __soc_setup_card_name(char *name, int len,
+ const char *name1, const char *name2,
+ int normalization)
{
- /* free the ALSA card at first; this syncs with pending operations */
- if (card->snd_card) {
- snd_card_free(card->snd_card);
- card->snd_card = NULL;
+ int i;
+
+ snprintf(name, len, "%s", name1 ? name1 : name2);
+
+ if (!normalization)
+ return;
+
+ /*
+ * Name normalization
+ *
+ * The driver name is somewhat special, as it's used as a key for
+ * searches in the user-space.
+ *
+ * ex)
+ * "abcd??efg" -> "abcd__efg"
+ */
+ for (i = 0; i < len; i++) {
+ switch (name[i]) {
+ case '_':
+ case '-':
+ case '\0':
+ break;
+ default:
+ if (!isalnum(name[i]))
+ name[i] = '_';
+ break;
+ }
}
+}
+
+static void soc_cleanup_card_resources(struct snd_soc_card *card,
+ int card_probed)
+{
+ struct snd_soc_dai_link *link, *_link;
+
+ if (card->snd_card)
+ snd_card_disconnect_sync(card->snd_card);
+
+ snd_soc_dapm_shutdown(card);
/* remove and free each DAI */
- soc_remove_dai_links(card);
- soc_remove_pcm_runtimes(card);
+ soc_remove_link_dais(card);
+ soc_remove_link_components(card);
+
+ for_each_card_links_safe(card, link, _link)
+ snd_soc_remove_dai_link(card, link);
/* remove auxiliary devices */
soc_remove_aux_devices(card);
@@ -1938,26 +1953,39 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card)
soc_cleanup_card_debugfs(card);
/* remove the card */
- if (card->remove)
+ if (card_probed && card->remove)
card->remove(card);
+
+ if (card->snd_card) {
+ snd_card_free(card->snd_card);
+ card->snd_card = NULL;
+ }
+}
+
+static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister)
+{
+ if (card->instantiated) {
+ int card_probed = 1;
+
+ card->instantiated = false;
+ snd_soc_flush_all_delayed_work(card);
+
+ soc_cleanup_card_resources(card, card_probed);
+ if (!unregister)
+ list_add(&card->list, &unbind_card_list);
+ } else {
+ if (unregister)
+ list_del(&card->list);
+ }
}
-static int snd_soc_instantiate_card(struct snd_soc_card *card)
+static int snd_soc_bind_card(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai_link *dai_link;
- int ret, i;
+ int ret, i, card_probed = 0;
mutex_lock(&client_mutex);
- for_each_card_prelinks(card, i, dai_link) {
- ret = soc_init_dai_link(card, dai_link);
- if (ret) {
- dev_err(card->dev, "ASoC: failed to init link %s: %d\n",
- dai_link->name, ret);
- mutex_unlock(&client_mutex);
- return ret;
- }
- }
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
snd_soc_dapm_init(&card->dapm, card, NULL);
@@ -1965,19 +1993,13 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
/* check whether any platform is ignore machine FE and using topology */
soc_check_tplg_fes(card);
- /* bind DAIs */
- for_each_card_prelinks(card, i, dai_link) {
- ret = soc_bind_dai_link(card, dai_link);
- if (ret != 0)
- goto probe_end;
- }
-
/* bind aux_devs too */
ret = soc_bind_aux_dev(card);
if (ret < 0)
goto probe_end;
/* add predefined DAI links to the list */
+ card->num_rtd = 0;
for_each_card_prelinks(card, i, dai_link) {
ret = snd_soc_add_dai_link(card, dai_link);
if (ret < 0)
@@ -2013,6 +2035,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
ret = card->probe(card);
if (ret < 0)
goto probe_end;
+ card_probed = 1;
}
/* probe all components used by DAI links on this card */
@@ -2025,23 +2048,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
/* probe auxiliary components */
ret = soc_probe_aux_devices(card);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(card->dev,
+ "ASoC: failed to probe aux component %d\n", ret);
goto probe_end;
-
- /*
- * Find new DAI links added during probing components and bind them.
- * Components with topology may bring new DAIs and DAI links.
- */
- for_each_card_links(card, dai_link) {
- if (soc_is_dai_link_bound(card, dai_link))
- continue;
-
- ret = soc_init_dai_link(card, dai_link);
- if (ret)
- goto probe_end;
- ret = soc_bind_dai_link(card, dai_link);
- if (ret)
- goto probe_end;
}
/* probe all DAI links on this card */
@@ -2076,22 +2086,23 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
/* try to set some sane longname if DMI is available */
snd_soc_set_dmi_name(card, NULL);
- snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
- "%s", card->name);
- snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
- "%s", card->long_name ? card->long_name : card->name);
- snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
- "%s", card->driver_name ? card->driver_name : card->name);
- for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) {
- switch (card->snd_card->driver[i]) {
- case '_':
- case '-':
- case '\0':
- break;
- default:
- if (!isalnum(card->snd_card->driver[i]))
- card->snd_card->driver[i] = '_';
- break;
+ soc_setup_card_name(card->snd_card->shortname,
+ card->name, NULL, 0);
+ soc_setup_card_name(card->snd_card->longname,
+ card->long_name, card->name, 0);
+ soc_setup_card_name(card->snd_card->driver,
+ card->driver_name, card->name, 1);
+
+ if (card->components) {
+ /* the current implementation of snd_component_add() accepts */
+ /* multiple components in the string separated by space, */
+ /* but the string collision (identical string) check might */
+ /* not work correctly */
+ ret = snd_component_add(card->snd_card, card->components);
+ if (ret < 0) {
+ dev_err(card->dev, "ASoC: %s snd_component_add() failed: %d\n",
+ card->name, ret);
+ goto probe_end;
}
}
@@ -2103,6 +2114,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
goto probe_end;
}
}
+ card_probed = 1;
snd_soc_dapm_new_widgets(card);
@@ -2117,9 +2129,22 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
+ /* deactivate pins to sleep state */
+ for_each_card_rtds(card, rtd) {
+ struct snd_soc_dai *dai;
+
+ for_each_rtd_codec_dai(rtd, i, dai) {
+ if (!dai->active)
+ pinctrl_pm_select_sleep_state(dai->dev);
+ }
+
+ if (!rtd->cpu_dai->active)
+ pinctrl_pm_select_sleep_state(rtd->cpu_dai->dev);
+ }
+
probe_end:
if (ret < 0)
- soc_cleanup_card_resources(card);
+ soc_cleanup_card_resources(card, card_probed);
mutex_unlock(&card->mutex);
mutex_unlock(&client_mutex);
@@ -2349,33 +2374,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
}
EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
-static int snd_soc_bind_card(struct snd_soc_card *card)
-{
- struct snd_soc_pcm_runtime *rtd;
- int ret;
-
- ret = snd_soc_instantiate_card(card);
- if (ret != 0)
- return ret;
-
- /* deactivate pins to sleep state */
- for_each_card_rtds(card, rtd) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai;
- int j;
-
- for_each_rtd_codec_dai(rtd, j, codec_dai) {
- if (!codec_dai->active)
- pinctrl_pm_select_sleep_state(codec_dai->dev);
- }
-
- if (!cpu_dai->active)
- pinctrl_pm_select_sleep_state(cpu_dai->dev);
- }
-
- return ret;
-}
-
/**
* snd_soc_register_card - Register a card with the ASoC core
*
@@ -2400,7 +2398,6 @@ int snd_soc_register_card(struct snd_soc_card *card)
INIT_LIST_HEAD(&card->dapm_dirty);
INIT_LIST_HEAD(&card->dobj_list);
- card->num_rtd = 0;
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
@@ -2411,25 +2408,6 @@ int snd_soc_register_card(struct snd_soc_card *card)
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);
-static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister)
-{
- if (card->instantiated) {
- card->instantiated = false;
- snd_soc_dapm_shutdown(card);
- snd_soc_flush_all_delayed_work(card);
-
- /* remove all components used by DAI links on this card */
- soc_remove_link_components(card);
-
- soc_cleanup_card_resources(card);
- if (!unregister)
- list_add(&card->list, &unbind_card_list);
- } else {
- if (unregister)
- list_del(&card->list);
- }
-}
-
/**
* snd_soc_unregister_card - Unregister a card with the ASoC core
*
@@ -2488,7 +2466,7 @@ static char *fmt_single_name(struct device *dev, int *id)
*id = 0;
}
- return kstrdup(name, GFP_KERNEL);
+ return devm_kstrdup(dev, name, GFP_KERNEL);
}
/*
@@ -2505,38 +2483,38 @@ static inline char *fmt_multiple_name(struct device *dev,
return NULL;
}
- return kstrdup(dai_drv->name, GFP_KERNEL);
+ return devm_kstrdup(dev, dai_drv->name, GFP_KERNEL);
}
-/**
- * snd_soc_unregister_dai - Unregister DAIs from the ASoC core
- *
- * @component: The component for which the DAIs should be unregistered
- */
-static void snd_soc_unregister_dais(struct snd_soc_component *component)
+void snd_soc_unregister_dai(struct snd_soc_dai *dai)
{
- struct snd_soc_dai *dai, *_dai;
-
- for_each_component_dais_safe(component, dai, _dai) {
- dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n",
- dai->name);
- list_del(&dai->list);
- kfree(dai->name);
- kfree(dai);
- }
+ dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name);
+ list_del(&dai->list);
}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
-/* Create a DAI and add it to the component's DAI list */
-static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component,
- struct snd_soc_dai_driver *dai_drv,
- bool legacy_dai_naming)
+/**
+ * snd_soc_register_dai - Register a DAI dynamically & create its widgets
+ *
+ * @component: The component the DAIs are registered for
+ * @dai_drv: DAI driver to use for the DAI
+ *
+ * Topology can use this API to register DAIs when probing a component.
+ * These DAIs's widgets will be freed in the card cleanup and the DAIs
+ * will be freed in the component cleanup.
+ */
+struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
+ struct snd_soc_dai_driver *dai_drv,
+ bool legacy_dai_naming)
{
struct device *dev = component->dev;
struct snd_soc_dai *dai;
dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev));
- dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
+ lockdep_assert_held(&client_mutex);
+
+ dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL);
if (dai == NULL)
return NULL;
@@ -2558,10 +2536,8 @@ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component,
else
dai->id = component->num_dai;
}
- if (dai->name == NULL) {
- kfree(dai);
+ if (!dai->name)
return NULL;
- }
dai->component = component;
dai->dev = dev;
@@ -2578,6 +2554,19 @@ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component,
}
/**
+ * snd_soc_unregister_dai - Unregister DAIs from the ASoC core
+ *
+ * @component: The component for which the DAIs should be unregistered
+ */
+static void snd_soc_unregister_dais(struct snd_soc_component *component)
+{
+ struct snd_soc_dai *dai, *_dai;
+
+ for_each_component_dais_safe(component, dai, _dai)
+ snd_soc_unregister_dai(dai);
+}
+
+/**
* snd_soc_register_dais - Register a DAI with the ASoC core
*
* @component: The component the DAIs are registered for
@@ -2588,16 +2577,12 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv,
size_t count)
{
- struct device *dev = component->dev;
struct snd_soc_dai *dai;
unsigned int i;
int ret;
- dev_dbg(dev, "ASoC: dai register %s #%zu\n", dev_name(dev), count);
-
for (i = 0; i < count; i++) {
-
- dai = soc_add_dai(component, dai_drv + i, count == 1 &&
+ dai = snd_soc_register_dai(component, dai_drv + i, count == 1 &&
!component->driver->non_legacy_dai_naming);
if (dai == NULL) {
ret = -ENOMEM;
@@ -2613,49 +2598,6 @@ err:
return ret;
}
-/**
- * snd_soc_register_dai - Register a DAI dynamically & create its widgets
- *
- * @component: The component the DAIs are registered for
- * @dai_drv: DAI driver to use for the DAI
- *
- * Topology can use this API to register DAIs when probing a component.
- * These DAIs's widgets will be freed in the card cleanup and the DAIs
- * will be freed in the component cleanup.
- */
-int snd_soc_register_dai(struct snd_soc_component *component,
- struct snd_soc_dai_driver *dai_drv)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- struct snd_soc_dai *dai;
- int ret;
-
- if (dai_drv->dobj.type != SND_SOC_DOBJ_PCM) {
- dev_err(component->dev, "Invalid dai type %d\n",
- dai_drv->dobj.type);
- return -EINVAL;
- }
-
- lockdep_assert_held(&client_mutex);
- dai = soc_add_dai(component, dai_drv, false);
- if (!dai)
- return -ENOMEM;
-
- /*
- * Create the DAI widgets here. After adding DAIs, topology may
- * also add routes that need these widgets as source or sink.
- */
- ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
- if (ret != 0) {
- dev_err(component->dev,
- "Failed to create DAI widgets %d\n", ret);
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_register_dai);
-
static int snd_soc_component_initialize(struct snd_soc_component *component,
const struct snd_soc_component_driver *driver, struct device *dev)
{
@@ -2726,40 +2668,6 @@ EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
#endif
-static void snd_soc_component_add(struct snd_soc_component *component)
-{
- mutex_lock(&client_mutex);
-
- if (!component->driver->write && !component->driver->read) {
- if (!component->regmap)
- component->regmap = dev_get_regmap(component->dev,
- NULL);
- if (component->regmap)
- snd_soc_component_setup_regmap(component);
- }
-
- /* see for_each_component */
- list_add(&component->list, &component_list);
-
- mutex_unlock(&client_mutex);
-}
-
-static void snd_soc_component_cleanup(struct snd_soc_component *component)
-{
- snd_soc_unregister_dais(component);
- kfree(component->name);
-}
-
-static void snd_soc_component_del_unlocked(struct snd_soc_component *component)
-{
- struct snd_soc_card *card = component->card;
-
- if (card)
- snd_soc_unbind_card(card, false);
-
- list_del(&component->list);
-}
-
#define ENDIANNESS_MAP(name) \
(SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE)
static u64 endianness_format_map[] = {
@@ -2804,6 +2712,18 @@ static void snd_soc_try_rebind_card(void)
list_del(&card->list);
}
+static void snd_soc_del_component_unlocked(struct snd_soc_component *component)
+{
+ struct snd_soc_card *card = component->card;
+
+ snd_soc_unregister_dais(component);
+
+ if (card)
+ snd_soc_unbind_card(card, false);
+
+ list_del(&component->list);
+}
+
int snd_soc_add_component(struct device *dev,
struct snd_soc_component *component,
const struct snd_soc_component_driver *component_driver,
@@ -2813,6 +2733,8 @@ int snd_soc_add_component(struct device *dev,
int ret;
int i;
+ mutex_lock(&client_mutex);
+
ret = snd_soc_component_initialize(component, component_driver, dev);
if (ret)
goto err_free;
@@ -2830,14 +2752,26 @@ int snd_soc_add_component(struct device *dev,
goto err_cleanup;
}
- snd_soc_component_add(component);
- snd_soc_try_rebind_card();
+ if (!component->driver->write && !component->driver->read) {
+ if (!component->regmap)
+ component->regmap = dev_get_regmap(component->dev,
+ NULL);
+ if (component->regmap)
+ snd_soc_component_setup_regmap(component);
+ }
- return 0;
+ /* see for_each_component */
+ list_add(&component->list, &component_list);
err_cleanup:
- snd_soc_component_cleanup(component);
+ if (ret < 0)
+ snd_soc_del_component_unlocked(component);
err_free:
+ mutex_unlock(&client_mutex);
+
+ if (ret == 0)
+ snd_soc_try_rebind_card();
+
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_add_component);
@@ -2864,62 +2798,21 @@ EXPORT_SYMBOL_GPL(snd_soc_register_component);
*
* @dev: The device to unregister
*/
-static int __snd_soc_unregister_component(struct device *dev)
-{
- struct snd_soc_component *component;
- int found = 0;
-
- mutex_lock(&client_mutex);
- for_each_component(component) {
- if (dev != component->dev)
- continue;
-
- snd_soc_tplg_component_remove(component,
- SND_SOC_TPLG_INDEX_ALL);
- snd_soc_component_del_unlocked(component);
- found = 1;
- break;
- }
- mutex_unlock(&client_mutex);
-
- if (found)
- snd_soc_component_cleanup(component);
-
- return found;
-}
-
void snd_soc_unregister_component(struct device *dev)
{
- while (__snd_soc_unregister_component(dev))
- ;
-}
-EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
-
-struct snd_soc_component *snd_soc_lookup_component(struct device *dev,
- const char *driver_name)
-{
struct snd_soc_component *component;
- struct snd_soc_component *ret;
- ret = NULL;
mutex_lock(&client_mutex);
- for_each_component(component) {
- if (dev != component->dev)
- continue;
-
- if (driver_name &&
- (driver_name != component->driver->name) &&
- (strcmp(component->driver->name, driver_name) != 0))
- continue;
+ while (1) {
+ component = snd_soc_lookup_component_nolocked(dev, NULL);
+ if (!component)
+ break;
- ret = component;
- break;
+ snd_soc_del_component_unlocked(component);
}
mutex_unlock(&client_mutex);
-
- return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_lookup_component);
+EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
/* Retrieve a card's name from device tree */
int snd_soc_of_parse_card_name(struct snd_soc_card *card,
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 5552c66ca642..a428ff393ea2 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -75,12 +75,10 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config);
-static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int dmaengine_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
int (*prepare_slave_config)(struct snd_pcm_substream *substream,
@@ -109,21 +107,16 @@ static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream,
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
}
-static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substream)
+static int
+dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
struct device *dma_dev = dmaengine_dma_dev(pcm, substream);
struct dma_chan *chan = pcm->chan[substream->stream];
struct snd_dmaengine_dai_dma_data *dma_data;
- struct dma_slave_caps dma_caps;
struct snd_pcm_hardware hw;
- u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
- BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
- BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
- snd_pcm_format_t i;
int ret;
if (pcm->config && pcm->config->pcm_hardware)
@@ -145,82 +138,53 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
hw.info |= SNDRV_PCM_INFO_BATCH;
- ret = dma_get_slave_caps(chan, &dma_caps);
- if (ret == 0) {
- if (dma_caps.cmd_pause && dma_caps.cmd_resume)
- hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
- if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
- hw.info |= SNDRV_PCM_INFO_BATCH;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- addr_widths = dma_caps.dst_addr_widths;
- else
- addr_widths = dma_caps.src_addr_widths;
- }
-
- /*
- * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
- * hw.formats set to 0, meaning no restrictions are in place.
- * In this case it's the responsibility of the DAI driver to
- * provide the supported format information.
- */
- if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
- /*
- * Prepare formats mask for valid/allowed sample types. If the
- * dma does not have support for the given physical word size,
- * it needs to be masked out so user space can not use the
- * format which produces corrupted audio.
- * In case the dma driver does not implement the slave_caps the
- * default assumption is that it supports 1, 2 and 4 bytes
- * widths.
- */
- for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) {
- int bits = snd_pcm_format_physical_width(i);
-
- /*
- * Enable only samples with DMA supported physical
- * widths
- */
- switch (bits) {
- case 8:
- case 16:
- case 24:
- case 32:
- case 64:
- if (addr_widths & (1 << (bits / 8)))
- hw.formats |= pcm_format_to_bits(i);
- break;
- default:
- /* Unsupported types */
- break;
- }
- }
+ ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream,
+ dma_data,
+ &hw,
+ chan);
+ if (ret)
+ return ret;
return snd_soc_set_runtime_hwparams(substream, &hw);
}
-static int dmaengine_pcm_open(struct snd_pcm_substream *substream)
+static int dmaengine_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
struct dma_chan *chan = pcm->chan[substream->stream];
int ret;
- ret = dmaengine_pcm_set_runtime_hwparams(substream);
+ ret = dmaengine_pcm_set_runtime_hwparams(component, substream);
if (ret)
return ret;
return snd_dmaengine_pcm_open(substream, chan);
}
+static int dmaengine_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return snd_dmaengine_pcm_close(substream);
+}
+
+static int dmaengine_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int dmaengine_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ return snd_dmaengine_pcm_trigger(substream, cmd);
+}
+
static struct dma_chan *dmaengine_pcm_compat_request_channel(
+ struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_substream *substream)
{
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
struct snd_dmaengine_dai_dma_data *dma_data;
dma_filter_fn fn = NULL;
@@ -258,10 +222,9 @@ static bool dmaengine_pcm_can_report_residue(struct device *dev,
return true;
}
-static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int dmaengine_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
const struct snd_dmaengine_pcm_config *config = pcm->config;
struct device *dev = component->dev;
@@ -288,8 +251,8 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
config->chan_names[i]);
if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
- pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
- substream);
+ pcm->chan[i] = dmaengine_pcm_compat_request_channel(
+ component, rtd, substream);
}
if (!pcm->chan[i]) {
@@ -318,11 +281,9 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
}
static snd_pcm_uframes_t dmaengine_pcm_pointer(
+ struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
@@ -331,13 +292,11 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
return snd_dmaengine_pcm_pointer(substream);
}
-static int dmaengine_copy_user(struct snd_pcm_substream *substream,
+static int dmaengine_copy_user(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
void __user *buf, unsigned long bytes)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
struct snd_pcm_runtime *runtime = substream->runtime;
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
int (*process)(struct snd_pcm_substream *substream,
@@ -365,39 +324,31 @@ static int dmaengine_copy_user(struct snd_pcm_substream *substream,
return 0;
}
-static const struct snd_pcm_ops dmaengine_pcm_ops = {
+static const struct snd_soc_component_driver dmaengine_pcm_component = {
+ .name = SND_DMAENGINE_PCM_DRV_NAME,
+ .probe_order = SND_SOC_COMP_ORDER_LATE,
.open = dmaengine_pcm_open,
- .close = snd_dmaengine_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
+ .close = dmaengine_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
.hw_params = dmaengine_pcm_hw_params,
- .hw_free = snd_pcm_lib_free_pages,
- .trigger = snd_dmaengine_pcm_trigger,
+ .hw_free = dmaengine_pcm_hw_free,
+ .trigger = dmaengine_pcm_trigger,
.pointer = dmaengine_pcm_pointer,
+ .pcm_construct = dmaengine_pcm_new,
};
-static const struct snd_pcm_ops dmaengine_pcm_process_ops = {
+static const struct snd_soc_component_driver dmaengine_pcm_component_process = {
+ .name = SND_DMAENGINE_PCM_DRV_NAME,
+ .probe_order = SND_SOC_COMP_ORDER_LATE,
.open = dmaengine_pcm_open,
- .close = snd_dmaengine_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
+ .close = dmaengine_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
.hw_params = dmaengine_pcm_hw_params,
- .hw_free = snd_pcm_lib_free_pages,
- .trigger = snd_dmaengine_pcm_trigger,
+ .hw_free = dmaengine_pcm_hw_free,
+ .trigger = dmaengine_pcm_trigger,
.pointer = dmaengine_pcm_pointer,
.copy_user = dmaengine_copy_user,
-};
-
-static const struct snd_soc_component_driver dmaengine_pcm_component = {
- .name = SND_DMAENGINE_PCM_DRV_NAME,
- .probe_order = SND_SOC_COMP_ORDER_LATE,
- .ops = &dmaengine_pcm_ops,
- .pcm_new = dmaengine_pcm_new,
-};
-
-static const struct snd_soc_component_driver dmaengine_pcm_component_process = {
- .name = SND_DMAENGINE_PCM_DRV_NAME,
- .probe_order = SND_SOC_COMP_ORDER_LATE,
- .ops = &dmaengine_pcm_process_ops,
- .pcm_new = dmaengine_pcm_new,
+ .pcm_construct = dmaengine_pcm_new,
};
static const char * const dmaengine_pcm_dma_channel_names[] = {
@@ -436,7 +387,7 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
name = dmaengine_pcm_dma_channel_names[i];
if (config && config->chan_names[i])
name = config->chan_names[i];
- chan = dma_request_slave_channel_reason(dev, name);
+ chan = dma_request_chan(dev, name);
if (IS_ERR(chan)) {
if (PTR_ERR(chan) == -EPROBE_DEFER)
return -EPROBE_DEFER;
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index f4dc3d445aae..652657dc6809 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -592,23 +592,16 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
int snd_soc_limit_volume(struct snd_soc_card *card,
const char *name, int max)
{
- struct snd_card *snd_card = card->snd_card;
struct snd_kcontrol *kctl;
struct soc_mixer_control *mc;
- int found = 0;
int ret = -EINVAL;
/* Sanity check for name and max */
if (unlikely(!name || max <= 0))
return -EINVAL;
- list_for_each_entry(kctl, &snd_card->controls, list) {
- if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
- found = 1;
- break;
- }
- }
- if (found) {
+ kctl = snd_soc_card_get_kcontrol(card, name);
+ if (kctl) {
mc = (struct soc_mixer_control *)kctl->private_value;
if (max <= mc->max) {
mc->platform_max = max;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index b600d3eaaf5c..01eb8700c3de 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -118,11 +118,8 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
return true;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component)
ignore &= !component->driver->use_pmdown_time;
- }
return ignore;
}
@@ -435,8 +432,7 @@ static int soc_pcm_components_open(struct snd_pcm_substream *substream,
struct snd_soc_component *component;
int ret = 0;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
+ for_each_rtd_components(rtd, rtdcom, component) {
*last = component;
ret = snd_soc_component_module_get_when_open(component);
@@ -467,9 +463,7 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream,
struct snd_soc_component *component;
int ret = 0;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (component == last)
break;
@@ -500,9 +494,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
for_each_rtd_codec_dai(rtd, i, codec_dai)
pinctrl_pm_select_default_state(codec_dai->dev);
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
pm_runtime_get_sync(component->dev);
}
@@ -625,9 +617,7 @@ component_err:
out:
mutex_unlock(&rtd->card->pcm_mutex);
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
}
@@ -740,9 +730,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
mutex_unlock(&rtd->card->pcm_mutex);
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
}
@@ -782,9 +770,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
}
}
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
ret = snd_soc_component_prepare(component, substream);
if (ret < 0) {
dev_err(component->dev,
@@ -849,9 +835,7 @@ static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_component *component;
int ret = 0;
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
if (component == last)
break;
@@ -877,6 +861,11 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
int i, ret = 0;
mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+
+ ret = soc_pcm_params_symmetry(substream, params);
+ if (ret)
+ goto out;
+
if (rtd->dai_link->ops->hw_params) {
ret = rtd->dai_link->ops->hw_params(substream, params);
if (ret < 0) {
@@ -945,9 +934,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
snd_soc_dapm_update_dai(substream, params, cpu_dai);
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
+ for_each_rtd_components(rtd, rtdcom, component) {
ret = snd_soc_component_hw_params(component, substream, params);
if (ret < 0) {
dev_err(component->dev,
@@ -958,9 +945,6 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
}
component = NULL;
- ret = soc_pcm_params_symmetry(substream, params);
- if (ret)
- goto component_err;
out:
mutex_unlock(&rtd->card->pcm_mutex);
return ret;
@@ -1047,7 +1031,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+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;
@@ -1056,16 +1040,42 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_soc_dai *codec_dai;
int i, ret;
+ if (rtd->dai_link->ops->trigger) {
+ ret = rtd->dai_link->ops->trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ for_each_rtd_components(rtd, rtdcom, component) {
+ ret = snd_soc_component_trigger(component, substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = snd_soc_dai_trigger(cpu_dai, substream, cmd);
+ if (ret < 0)
+ return ret;
+
for_each_rtd_codec_dai(rtd, i, codec_dai) {
ret = snd_soc_dai_trigger(codec_dai, substream, cmd);
if (ret < 0)
return ret;
}
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
+ return 0;
+}
- ret = snd_soc_component_trigger(component, substream, cmd);
+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_rtdcom_list *rtdcom;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai;
+ int i, ret;
+
+ for_each_rtd_codec_dai(rtd, i, codec_dai) {
+ ret = snd_soc_dai_trigger(codec_dai, substream, cmd);
if (ret < 0)
return ret;
}
@@ -1074,6 +1084,12 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
if (ret < 0)
return ret;
+ for_each_rtd_components(rtd, rtdcom, component) {
+ ret = snd_soc_component_trigger(component, substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
if (rtd->dai_link->ops->trigger) {
ret = rtd->dai_link->ops->trigger(substream, cmd);
if (ret < 0)
@@ -1083,6 +1099,28 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
+static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = soc_pcm_trigger_start(substream, cmd);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = soc_pcm_trigger_stop(substream, cmd);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
int cmd)
{
@@ -1146,7 +1184,9 @@ 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) {
@@ -1385,6 +1425,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
struct snd_soc_dapm_widget *widget;
struct snd_soc_dai *dai;
int prune = 0;
+ int do_prune;
/* Destroy any old FE <--> BE connections */
for_each_dpcm_be(fe, stream, dpcm) {
@@ -1398,13 +1439,16 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
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);
/* prune the BE if it's no longer in our active list */
if (widget && widget_in_list(list, widget))
- continue;
+ do_prune = 0;
}
+ if (!do_prune)
+ continue;
dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
stream ? "capture" : "playback",
@@ -2289,42 +2333,81 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
}
EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
+static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream,
+ int cmd, bool fe_first)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int ret;
+
+ /* call trigger on the frontend before the backend. */
+ if (fe_first) {
+ dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
+ fe->dai_link->name, cmd);
+
+ ret = soc_pcm_trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+ return ret;
+ }
+
+ /* call trigger on the frontend after the backend. */
+ ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
+ fe->dai_link->name, cmd);
+
+ ret = soc_pcm_trigger(substream, cmd);
+
+ return ret;
+}
+
static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
- int stream = substream->stream, ret;
+ int stream = substream->stream;
+ int ret = 0;
enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
switch (trigger) {
case SND_SOC_DPCM_TRIGGER_PRE:
- /* call trigger on the frontend before the backend. */
-
- dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
- fe->dai_link->name, cmd);
-
- ret = soc_pcm_trigger(substream, cmd);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
- goto out;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
-
- ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
break;
case SND_SOC_DPCM_TRIGGER_POST:
- /* call trigger on the frontend after the backend. */
-
- ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
- goto out;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
-
- dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
- fe->dai_link->name, cmd);
-
- ret = soc_pcm_trigger(substream, cmd);
break;
case SND_SOC_DPCM_TRIGGER_BESPOKE:
/* bespoke trigger() - handles both FE and BEs */
@@ -2333,10 +2416,6 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
fe->dai_link->name, cmd);
ret = soc_pcm_bespoke_trigger(substream, cmd);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
- goto out;
- }
break;
default:
dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd,
@@ -2345,6 +2424,12 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
goto out;
}
+ if (ret < 0) {
+ dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n",
+ cmd, ret);
+ goto out;
+ }
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -2809,21 +2894,13 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
return ret;
}
-static void soc_pcm_private_free(struct snd_pcm *pcm)
-{
- struct snd_soc_pcm_runtime *rtd = pcm->private_data;
-
- /* need to sync the delayed work before releasing resources */
- flush_delayed_work(&rtd->delayed_work);
- snd_soc_pcm_component_free(pcm);
-}
-
/* create a new pcm */
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_rtdcom_list *rtdcom;
+ struct snd_soc_component *component;
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
@@ -2935,17 +3012,14 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
rtd->ops.ioctl = snd_soc_pcm_component_ioctl;
}
- for_each_rtdcom(rtd, rtdcom) {
- const struct snd_pcm_ops *ops = rtdcom->component->driver->ops;
-
- if (!ops)
- continue;
+ for_each_rtd_components(rtd, rtdcom, component) {
+ const struct snd_soc_component_driver *drv = component->driver;
- if (ops->copy_user)
+ if (drv->copy_user)
rtd->ops.copy_user = snd_soc_pcm_component_copy_user;
- if (ops->page)
+ if (drv->page)
rtd->ops.page = snd_soc_pcm_component_page;
- if (ops->mmap)
+ if (drv->mmap)
rtd->ops.mmap = snd_soc_pcm_component_mmap;
}
@@ -2955,13 +3029,12 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
- ret = snd_soc_pcm_component_new(pcm);
+ ret = snd_soc_pcm_component_new(rtd);
if (ret < 0) {
dev_err(rtd->dev, "ASoC: pcm constructor failed: %d\n", ret);
return ret;
}
- pcm->private_free = soc_pcm_private_free;
pcm->no_device_suspend = true;
out:
dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 0fd032914a31..81d2af000a5c 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1800,6 +1800,9 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
struct snd_soc_dai_driver *dai_drv;
struct snd_soc_pcm_stream *stream;
struct snd_soc_tplg_stream_caps *caps;
+ struct snd_soc_dai *dai;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(tplg->comp);
int ret;
dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
@@ -1842,7 +1845,19 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list);
/* register the DAI to the component */
- return snd_soc_register_dai(tplg->comp, dai_drv);
+ dai = snd_soc_register_dai(tplg->comp, dai_drv, false);
+ if (!dai)
+ return -ENOMEM;
+
+ /* Create the DAI widgets here */
+ ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
+ if (ret != 0) {
+ dev_err(dai->dev, "Failed to create DAI widgets %d\n", ret);
+ snd_soc_unregister_dai(dai);
+ return ret;
+ }
+
+ return ret;
}
static void set_link_flags(struct snd_soc_dai_link *link,
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 54dcece52b0c..2fd4562f5e63 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -63,7 +63,8 @@ static const struct snd_pcm_hardware dummy_dma_hardware = {
.periods_max = 128,
};
-static int dummy_dma_open(struct snd_pcm_substream *substream)
+static int dummy_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -74,13 +75,9 @@ static int dummy_dma_open(struct snd_pcm_substream *substream)
return 0;
}
-static const struct snd_pcm_ops snd_dummy_dma_ops = {
- .open = dummy_dma_open,
- .ioctl = snd_pcm_lib_ioctl,
-};
-
static const struct snd_soc_component_driver dummy_platform = {
- .ops = &snd_dummy_dma_ops,
+ .open = dummy_dma_open,
+ .ioctl = snd_soc_pcm_lib_ioctl,
};
static const struct snd_soc_component_driver dummy_codec = {
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig
index bb8036ae567e..71a0fc075a63 100644
--- a/sound/soc/sof/Kconfig
+++ b/sound/soc/sof/Kconfig
@@ -14,8 +14,6 @@ config SND_SOC_SOF_PCI
depends on PCI
select SND_SOC_SOF
select SND_SOC_ACPI if ACPI
- select SND_SOC_SOF_OPTIONS
- select SND_SOC_SOF_INTEL_PCI if SND_SOC_SOF_INTEL_TOPLEVEL
help
This adds support for PCI enumeration. This option is
required to enable Intel Skylake+ devices
@@ -27,8 +25,6 @@ config SND_SOC_SOF_ACPI
depends on ACPI || COMPILE_TEST
select SND_SOC_SOF
select SND_SOC_ACPI if ACPI
- select SND_SOC_SOF_OPTIONS
- select SND_SOC_SOF_INTEL_ACPI if SND_SOC_SOF_INTEL_TOPLEVEL
select IOSF_MBI if X86 && PCI
help
This adds support for ACPI enumeration. This option is required
@@ -40,19 +36,23 @@ config SND_SOC_SOF_OF
tristate "SOF OF enumeration support"
depends on OF || COMPILE_TEST
select SND_SOC_SOF
- select SND_SOC_SOF_OPTIONS
help
This adds support for Device Tree enumeration. This option is
required to enable i.MX8 devices.
Say Y if you need this option. If unsure select "N".
-config SND_SOC_SOF_OPTIONS
- tristate
+config SND_SOC_SOF_DEVELOPER_SUPPORT
+ bool "SOF developer options support"
+ depends on EXPERT
help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level
+ This option unlock SOF developer options for debug/performance/
+ code hardening.
+ Distributions should not select this option, only SOF development
+ teams should select it.
+ Say Y if you are involved in SOF development and need this option
+ If not, select N
-if SND_SOC_SOF_OPTIONS
+if SND_SOC_SOF_DEVELOPER_SUPPORT
config SND_SOC_SOF_NOCODEC
tristate
@@ -64,6 +64,11 @@ config SND_SOC_SOF_NOCODEC_SUPPORT
option if no known codec is detected. This is typically only
enabled for developers or devices where the sound card is
controlled externally
+ This option is mutually exclusive with the Intel HDaudio support,
+ selecting it may have negative impacts and prevent e.g. microphone
+ functionality from being enabled on Intel CoffeeLake and later
+ platforms.
+ Distributions should not select this option!
Say Y if you need this nocodec fallback option
If unsure select "N".
@@ -142,6 +147,14 @@ config SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE
Say Y if you want to enable caching the memory windows.
If unsure, select "N".
+config SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE
+ bool "SOF enable firmware trace"
+ help
+ The firmware trace can be enabled either at build-time with
+ this option, or dynamically by setting flags in the SOF core
+ module parameter (similar to dynamic debug)
+ If unsure, select "N".
+
config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST
bool "SOF enable IPC flood test"
help
@@ -150,9 +163,17 @@ config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST
Say Y if you want to enable IPC flood test.
If unsure, select "N".
+config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT
+ bool "SOF retain DSP context on any FW exceptions"
+ help
+ This option keeps the DSP in D0 state so that firmware debug
+ information can be retained and dumped to userspace.
+ Say Y if you want to retain DSP context for FW exceptions.
+ If unsure, select "N".
+
endif ## SND_SOC_SOF_DEBUG
-endif ## SND_SOC_SOF_OPTIONS
+endif ## SND_SOC_SOF_DEVELOPER_SUPPORT
config SND_SOC_SOF
tristate
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index 2b8711eda362..7baf7f1507c3 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -11,8 +11,39 @@
/* Mixer Controls */
#include <linux/pm_runtime.h>
+#include <linux/leds.h>
#include "sof-priv.h"
+static void update_mute_led(struct snd_sof_control *scontrol,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int temp = 0;
+ unsigned int mask;
+ int i;
+
+ mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ for (i = 0; i < scontrol->num_channels; i++) {
+ if (ucontrol->value.integer.value[i]) {
+ temp |= mask;
+ break;
+ }
+ }
+
+ if (temp == scontrol->led_ctl.led_value)
+ return;
+
+ scontrol->led_ctl.led_value = temp;
+
+#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
+ if (!scontrol->led_ctl.direction)
+ ledtrig_audio_set(LED_AUDIO_MUTE, temp ? LED_OFF : LED_ON);
+ else
+ ledtrig_audio_set(LED_AUDIO_MICMUTE, temp ? LED_OFF : LED_ON);
+#endif
+}
+
static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
{
if (value >= size)
@@ -118,6 +149,9 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
cdata->chanv[i].value = value;
}
+ if (scontrol->led_ctl.use_led)
+ update_mute_led(scontrol, kcontrol, ucontrol);
+
/* notify DSP of mixer updates */
if (pm_runtime_active(sdev->dev))
snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 81f28f7ff1a0..805918d3bcc0 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -16,6 +16,11 @@
#include "sof-priv.h"
#include "ops.h"
+/* see SOF_DBG_ flags */
+int sof_core_debug;
+module_param_named(sof_debug, sof_core_debug, int, 0444);
+MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)");
+
/* SOF defaults if not provided by the platform in ms */
#define TIMEOUT_DEFAULT_IPC_MS 500
#define TIMEOUT_DEFAULT_BOOT_MS 2000
@@ -127,6 +132,19 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev,
return NULL;
}
+bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pcm *spcm;
+
+ list_for_each_entry(spcm, &sdev->pcm_list, list) {
+ if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
+ spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
+ return true;
+ }
+
+ return false;
+}
+
/*
* FW Panic/fault handling.
*/
@@ -350,12 +368,20 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
goto fw_run_err;
}
- /* init DMA trace */
- ret = snd_sof_init_trace(sdev);
- if (ret < 0) {
- /* non fatal */
- dev_warn(sdev->dev,
- "warning: failed to initialize trace %d\n", ret);
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE) ||
+ (sof_core_debug & SOF_DBG_ENABLE_TRACE)) {
+ sdev->dtrace_is_supported = true;
+
+ /* init DMA trace */
+ ret = snd_sof_init_trace(sdev);
+ if (ret < 0) {
+ /* non fatal */
+ dev_warn(sdev->dev,
+ "warning: failed to initialize trace %d\n",
+ ret);
+ }
+ } else {
+ dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
}
/* hereafter all FW boot flows are for PM reasons */
@@ -445,6 +471,9 @@ 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;
+
sdev->pdata = plat_data;
sdev->first_boot = true;
dev_set_drvdata(dev, sdev);
@@ -453,7 +482,8 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
!sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
!sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
- !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params)
+ !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params ||
+ !sof_ops(sdev)->fw_ready)
return -EINVAL;
INIT_LIST_HEAD(&sdev->pcm_list);
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index 5529e8eeca46..d2b3b99d3a20 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -463,3 +463,19 @@ void snd_sof_free_debug(struct snd_sof_dev *sdev)
debugfs_remove_recursive(sdev->debugfs_root);
}
EXPORT_SYMBOL_GPL(snd_sof_free_debug);
+
+void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev)
+{
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) ||
+ (sof_core_debug & SOF_DBG_RETAIN_CTX)) {
+ /* should we prevent DSP entering D3 ? */
+ dev_info(sdev->dev, "info: preventing DSP entering D3 state to preserve context\n");
+ pm_runtime_get_noresume(sdev->dev);
+ }
+
+ /* dump vital information to the logs */
+ snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX);
+ snd_sof_ipc_dump(sdev);
+ snd_sof_trace_notify_for_error(sdev);
+}
+EXPORT_SYMBOL(snd_sof_handle_fw_exception);
diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig
index 5acae75f5750..71f318bc2c74 100644
--- a/sound/soc/sof/imx/Kconfig
+++ b/sound/soc/sof/imx/Kconfig
@@ -11,8 +11,8 @@ config SND_SOC_SOF_IMX_TOPLEVEL
if SND_SOC_SOF_IMX_TOPLEVEL
-config SND_SOC_SOF_IMX8
- tristate "SOF support for i.MX8"
+config SND_SOC_SOF_IMX8_SUPPORT
+ bool "SOF support for i.MX8"
depends on IMX_SCU
depends on IMX_DSP
help
@@ -20,4 +20,8 @@ config SND_SOC_SOF_IMX8
Say Y if you have such a device.
If unsure select "N".
+config SND_SOC_SOF_IMX8
+ def_tristate SND_SOC_SOF_OF
+ depends on SND_SOC_SOF_IMX8_SUPPORT
+
endif ## SND_SOC_SOF_IMX_IMX_TOPLEVEL
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index 2a22b18e5ec0..cfefcfd92798 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -388,6 +388,13 @@ 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 */
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
};
EXPORT_SYMBOL(sof_imx8_ops);
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index d62f51d33be1..92f7485b6994 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -10,7 +10,7 @@ config SND_SOC_SOF_INTEL_TOPLEVEL
if SND_SOC_SOF_INTEL_TOPLEVEL
config SND_SOC_SOF_INTEL_ACPI
- tristate
+ def_tristate SND_SOC_SOF_ACPI
select SND_SOC_SOF_BAYTRAIL if SND_SOC_SOF_BAYTRAIL_SUPPORT
select SND_SOC_SOF_BROADWELL if SND_SOC_SOF_BROADWELL_SUPPORT
help
@@ -18,7 +18,7 @@ config SND_SOC_SOF_INTEL_ACPI
'select' statements at a higher level
config SND_SOC_SOF_INTEL_PCI
- tristate
+ def_tristate SND_SOC_SOF_PCI
select SND_SOC_SOF_MERRIFIELD if SND_SOC_SOF_MERRIFIELD_SUPPORT
select SND_SOC_SOF_APOLLOLAKE if SND_SOC_SOF_APOLLOLAKE_SUPPORT
select SND_SOC_SOF_GEMINILAKE if SND_SOC_SOF_GEMINILAKE_SUPPORT
@@ -29,6 +29,7 @@ config SND_SOC_SOF_INTEL_PCI
select SND_SOC_SOF_COMETLAKE_H if SND_SOC_SOF_COMETLAKE_H_SUPPORT
select SND_SOC_SOF_TIGERLAKE if SND_SOC_SOF_TIGERLAKE_SUPPORT
select SND_SOC_SOF_ELKHARTLAKE if SND_SOC_SOF_ELKHARTLAKE_SUPPORT
+ select SND_SOC_SOF_JASPERLAKE if SND_SOC_SOF_JASPERLAKE_SUPPORT
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level
@@ -61,10 +62,18 @@ if SND_SOC_SOF_INTEL_ACPI
config SND_SOC_SOF_BAYTRAIL_SUPPORT
bool "SOF support for Baytrail, Braswell and Cherrytrail"
+ depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI=n
help
This adds support for Sound Open Firmware for Intel(R) platforms
using the Baytrail, Braswell or Cherrytrail processors.
- Say Y if you have such a device.
+ This option is mutually exclusive with the Atom/SST and Baytrail
+ legacy drivers. If you want to enable SOF on Baytrail/Cherrytrail,
+ you need to deselect those options first.
+ SOF does not support Baytrail-CR for now, so this option is not
+ recommended for distros. At some point all legacy drivers will be
+ deprecated but not before all userspace firmware/topology/UCM files
+ are made available to downstream distros.
+ Say Y if you want to enable SOF on Baytrail/Cherrytrail
If unsure select "N".
config SND_SOC_SOF_BAYTRAIL
@@ -76,10 +85,18 @@ config SND_SOC_SOF_BAYTRAIL
config SND_SOC_SOF_BROADWELL_SUPPORT
bool "SOF support for Broadwell"
+ depends on SND_SOC_INTEL_HASWELL=n
help
This adds support for Sound Open Firmware for Intel(R) platforms
using the Broadwell processors.
- Say Y if you have such a device.
+ This option is mutually exclusive with the Haswell/Broadwell legacy
+ driver. If you want to enable SOF on Broadwell you need to deselect
+ the legacy driver first.
+ SOF does fully support Broadwell yet, so this option is not
+ recommended for distros. At some point all legacy drivers will be
+ deprecated but not before all userspace firmware/topology/UCM files
+ are made available to downstream distros.
+ Say Y if you want to enable SOF on Broadwell
If unsure select "N".
config SND_SOC_SOF_BROADWELL
@@ -244,6 +261,21 @@ config SND_SOC_SOF_ELKHARTLAKE
This option is not user-selectable but automagically handled by
'select' statements at a higher level
+config SND_SOC_SOF_JASPERLAKE_SUPPORT
+ bool "SOF support for JasperLake"
+ help
+ This adds support for Sound Open Firmware for Intel(R) platforms
+ using the JasperLake processors.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_SOF_JASPERLAKE
+ tristate
+ select SND_SOC_SOF_HDA_COMMON
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level
+
config SND_SOC_SOF_HDA_COMMON
tristate
select SND_SOC_SOF_INTEL_COMMON
@@ -283,6 +315,16 @@ config SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1
Say Y if you want to enable DMI Link L1
If unsure, select "N".
+config SND_SOC_SOF_HDA_COMMON_HDMI_CODEC
+ bool "SOF common HDA HDMI codec driver"
+ depends on SND_SOC_SOF_HDA_LINK
+ depends on SND_HDA_CODEC_HDMI
+ help
+ This adds support for HDMI audio by using the common HDA
+ HDMI/DisplayPort codec driver.
+ Say Y if you want to use the common codec driver with SOF.
+ If unsure select "Y".
+
endif ## SND_SOC_SOF_HDA_COMMON
config SND_SOC_SOF_HDA_LINK_BASELINE
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 8dc7a5558da4..7daa8eb456c8 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -97,6 +97,14 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.runtime_resume = hda_dsp_runtime_resume,
.runtime_idle = hda_dsp_runtime_idle,
.set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
+ .set_power_state = hda_dsp_set_power_state,
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
};
EXPORT_SYMBOL(sof_apl_ops);
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index 80e2826fb447..141dad554764 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -247,7 +247,7 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags)
struct sof_ipc_dsp_oops_xtensa xoops;
struct sof_ipc_panic_info panic_info;
u32 stack[BDW_STACK_DUMP_SIZE];
- u32 status, panic;
+ u32 status, panic, imrx, imrd;
/* now try generic SOF status messages */
status = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD);
@@ -256,6 +256,26 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags)
BDW_STACK_DUMP_SIZE);
snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
BDW_STACK_DUMP_SIZE);
+
+ /* provide some context for firmware debug */
+ imrx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRX);
+ imrd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRD);
+ dev_err(sdev->dev,
+ "error: ipc host -> DSP: pending %s complete %s raw 0x%8.8x\n",
+ (panic & SHIM_IPCX_BUSY) ? "yes" : "no",
+ (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
+ dev_err(sdev->dev,
+ "error: mask host: pending %s complete %s raw 0x%8.8x\n",
+ (imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
+ (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
+ dev_err(sdev->dev,
+ "error: ipc DSP -> host: pending %s complete %s raw 0x%8.8x\n",
+ (status & SHIM_IPCD_BUSY) ? "yes" : "no",
+ (status & SHIM_IPCD_DONE) ? "yes" : "no", status);
+ dev_err(sdev->dev,
+ "error: mask DSP: pending %s complete %s raw 0x%8.8x\n",
+ (imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
+ (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
}
/*
@@ -571,7 +591,14 @@ const struct snd_sof_dsp_ops sof_bdw_ops = {
/* DAI drivers */
.drv = bdw_dai,
- .num_drv = ARRAY_SIZE(bdw_dai)
+ .num_drv = ARRAY_SIZE(bdw_dai),
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH,
};
EXPORT_SYMBOL(sof_bdw_ops);
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index a1e514f71739..2abf80b3eb52 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -145,7 +145,7 @@ static void byt_dump(struct snd_sof_dev *sdev, u32 flags)
struct sof_ipc_dsp_oops_xtensa xoops;
struct sof_ipc_panic_info panic_info;
u32 stack[BYT_STACK_DUMP_SIZE];
- u32 status, panic;
+ u32 status, panic, imrd, imrx;
/* now try generic SOF status messages */
status = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCD);
@@ -154,6 +154,27 @@ static void byt_dump(struct snd_sof_dev *sdev, u32 flags)
BYT_STACK_DUMP_SIZE);
snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
BYT_STACK_DUMP_SIZE);
+
+ /* provide some context for firmware debug */
+ imrx = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IMRX);
+ imrd = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IMRD);
+ dev_err(sdev->dev,
+ "error: ipc host -> DSP: pending %s complete %s raw 0x%8.8x\n",
+ (panic & SHIM_IPCX_BUSY) ? "yes" : "no",
+ (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
+ dev_err(sdev->dev,
+ "error: mask host: pending %s complete %s raw 0x%8.8x\n",
+ (imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
+ (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
+ dev_err(sdev->dev,
+ "error: ipc DSP -> host: pending %s complete %s raw 0x%8.8x\n",
+ (status & SHIM_IPCD_BUSY) ? "yes" : "no",
+ (status & SHIM_IPCD_DONE) ? "yes" : "no", status);
+ dev_err(sdev->dev,
+ "error: mask DSP: pending %s complete %s raw 0x%8.8x\n",
+ (imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
+ (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
+
}
/*
@@ -511,6 +532,13 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
/* DAI drivers */
.drv = byt_dai,
.num_drv = 3, /* we have only 3 SSPs on byt*/
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH,
};
EXPORT_SYMBOL(sof_tng_ops);
@@ -672,6 +700,13 @@ const struct snd_sof_dsp_ops sof_byt_ops = {
/* DAI drivers */
.drv = byt_dai,
.num_drv = 3, /* we have only 3 SSPs on byt*/
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH,
};
EXPORT_SYMBOL(sof_byt_ops);
@@ -732,6 +767,13 @@ const struct snd_sof_dsp_ops sof_cht_ops = {
.drv = byt_dai,
/* all 6 SSPs may be available for cherrytrail */
.num_drv = ARRAY_SIZE(byt_dai),
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH,
};
EXPORT_SYMBOL(sof_cht_ops);
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 4ddd73762d81..0e1e265f3f3b 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -17,6 +17,7 @@
#include "../ops.h"
#include "hda.h"
+#include "hda-ipc.h"
static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = {
{"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS},
@@ -150,14 +151,45 @@ static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev)
CNL_DSP_REG_HIPCCTL_DONE);
}
+static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg,
+ u32 *dr, u32 *dd)
+{
+ struct sof_ipc_pm_gate *pm_gate;
+
+ if (msg->header == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
+ pm_gate = msg->msg_data;
+
+ /* send the compact message via the primary register */
+ *dr = HDA_IPC_MSG_COMPACT | HDA_IPC_PM_GATE;
+
+ /* send payload via the extended data register */
+ *dd = pm_gate->flags;
+
+ return true;
+ }
+
+ return false;
+}
+
static int cnl_ipc_send_msg(struct snd_sof_dev *sdev,
struct snd_sof_ipc_msg *msg)
{
- /* send the message */
- 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);
+ u32 dr = 0;
+ u32 dd = 0;
+
+ 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;
}
@@ -255,6 +287,14 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.runtime_resume = hda_dsp_runtime_resume,
.runtime_idle = hda_dsp_runtime_idle,
.set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
+ .set_power_state = hda_dsp_set_power_state,
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
};
EXPORT_SYMBOL(sof_cnl_ops);
@@ -327,3 +367,20 @@ const struct sof_intel_dsp_desc ehl_chip_info = {
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
};
EXPORT_SYMBOL(ehl_chip_info);
+
+const struct sof_intel_dsp_desc jsl_chip_info = {
+ /* Jasperlake */
+ .cores_num = 2,
+ .init_core_mask = 1,
+ .cores_mask = HDA_DSP_CORE_MASK(0) |
+ HDA_DSP_CORE_MASK(1),
+ .ipc_req = CNL_DSP_REG_HIPCIDR,
+ .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
+ .ipc_ack = CNL_DSP_REG_HIPCIDA,
+ .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
+ .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_init_timeout = 300,
+ .ssp_count = ICL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+};
+EXPORT_SYMBOL(jsl_chip_info);
diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c
index 3ca6795a89ba..827f84a0722e 100644
--- a/sound/soc/sof/intel/hda-codec.c
+++ b/sound/soc/sof/intel/hda-codec.c
@@ -84,6 +84,8 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address)
{
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
struct hdac_hda_priv *hda_priv;
+ struct snd_soc_acpi_mach_params *mach_params = NULL;
+ struct snd_sof_pdata *pdata = sdev->pdata;
#endif
struct hda_bus *hbus = sof_to_hbus(sdev);
struct hdac_device *hdev;
@@ -113,8 +115,19 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address)
if (ret < 0)
return ret;
- /* use legacy bus only for HDA codecs, idisp uses ext bus */
- if ((resp & 0xFFFF0000) != IDISP_VID_INTEL) {
+ if (pdata->machine)
+ mach_params = (struct snd_soc_acpi_mach_params *)
+ &pdata->machine->mach_params;
+
+ if ((resp & 0xFFFF0000) == IDISP_VID_INTEL)
+ hda_priv->need_display_power = true;
+
+ /*
+ * if common HDMI codec driver is not used, codec load
+ * is skipped here and hdac_hdmi is used instead
+ */
+ if ((mach_params && mach_params->common_hdmi_codec_drv) ||
+ (resp & 0xFFFF0000) != IDISP_VID_INTEL) {
hdev->type = HDA_DEV_LEGACY;
hda_codec_load_module(&hda_priv->codec);
}
@@ -155,7 +168,8 @@ int hda_codec_probe_bus(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL(hda_codec_probe_bus);
-#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)
+#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \
+ IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)
void hda_codec_i915_get(struct snd_sof_dev *sdev)
{
@@ -204,6 +218,6 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL(hda_codec_i915_exit);
-#endif /* CONFIG_SND_SOC_HDAC_HDMI */
+#endif
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index fb55a3c5afd0..4a4d318f97ff 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -19,6 +19,7 @@
#include <sound/hda_register.h>
#include "../ops.h"
#include "hda.h"
+#include "hda-ipc.h"
/*
* DSP Core control.
@@ -42,6 +43,12 @@ int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask)
((adspcs & reset) == reset),
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_RESET_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n",
+ __func__);
+ return ret;
+ }
/* has core entered reset ? */
adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
@@ -77,6 +84,13 @@ int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask)
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_RESET_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n",
+ __func__);
+ return ret;
+ }
+
/* has core left reset ? */
adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
HDA_DSP_REG_ADSPCS);
@@ -151,8 +165,12 @@ int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask)
(adspcs & cpa) == cpa,
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_RESET_TIMEOUT_US);
- if (ret < 0)
- dev_err(sdev->dev, "error: timeout on core powerup\n");
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n",
+ __func__);
+ return ret;
+ }
/* did core power up ? */
adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
@@ -171,17 +189,24 @@ int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask)
int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask)
{
u32 adspcs;
+ int ret;
/* update bits */
snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
HDA_DSP_REG_ADSPCS,
HDA_DSP_ADSPCS_SPA_MASK(core_mask), 0);
- return snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
HDA_DSP_REG_ADSPCS, adspcs,
!(adspcs & HDA_DSP_ADSPCS_SPA_MASK(core_mask)),
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC);
+ if (ret < 0)
+ dev_err(sdev->dev,
+ "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n",
+ __func__);
+
+ return ret;
}
bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev,
@@ -282,6 +307,80 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev)
HDA_DSP_REG_HIPCCTL_BUSY | HDA_DSP_REG_HIPCCTL_DONE, 0);
}
+static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ int retry = HDA_DSP_REG_POLL_RETRY_COUNT;
+
+ while (snd_hdac_chip_readb(bus, VS_D0I3C) & SOF_HDA_VS_D0I3C_CIP) {
+ if (!retry--)
+ return -ETIMEDOUT;
+ usleep_range(10, 15);
+ }
+
+ return 0;
+}
+
+static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags)
+{
+ struct sof_ipc_pm_gate pm_gate;
+ struct sof_ipc_reply reply;
+
+ memset(&pm_gate, 0, sizeof(pm_gate));
+
+ /* configure pm_gate ipc message */
+ pm_gate.hdr.size = sizeof(pm_gate);
+ pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
+ 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));
+}
+
+int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
+ enum sof_d0_substate d0_substate)
+{
+ 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);
+ if (ret < 0) {
+ dev_err(bus->dev, "CIP timeout before D0I3C update!\n");
+ return ret;
+ }
+
+ /* 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 */
+ ret = hda_dsp_wait_d0i3c_done(sdev);
+ if (ret < 0) {
+ dev_err(bus->dev, "CIP timeout after D0I3C update!\n");
+ return ret;
+ }
+
+ 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*/
+
+ /* sending pm_gate IPC */
+ ret = hda_dsp_send_pm_gate_ipc(sdev, flags);
+ if (ret < 0)
+ dev_err(sdev->dev,
+ "error: PM_GATE ipc error %d\n", ret);
+
+ return ret;
+}
+
static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -379,6 +478,22 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
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);
+
+ if (sdev->s0_suspend) {
+ /* restore L1SEN bit */
+ if (hda->l1_support_changed)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, 0);
+
+ /* restore and disable the system wakeup */
+ pci_restore_state(pci);
+ disable_irq_wake(pci->irq);
+ return 0;
+ }
+
/* init hda controller. DSP cores will be powered up during fw boot */
return hda_resume(sdev, false);
}
@@ -410,9 +525,25 @@ int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev)
int hda_dsp_suspend(struct snd_sof_dev *sdev)
{
+ 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);
int ret;
+ if (sdev->s0_suspend) {
+ /* enable L1SEN to make sure the system can enter S0Ix */
+ hda->l1_support_changed =
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN,
+ HDA_VS_INTEL_EM2_L1SEN);
+
+ /* enable the system waking up via IPC IRQ */
+ enable_irq_wake(pci->irq);
+ pci_save_state(pci);
+ return 0;
+ }
+
/* stop hda controller and power dsp off */
ret = hda_suspend(sdev, false);
if (ret < 0) {
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index 6aae6f18b3dc..0fd2153c1769 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -83,10 +83,12 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
}
hdr = msg->msg_data;
- if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) {
+ if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) ||
+ hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
/*
* memory windows are powered off before sending IPC reply,
- * so we can't read the mailbox for CTX_SAVE reply.
+ * so we can't read the mailbox for CTX_SAVE and PM_GATE
+ * replies.
*/
reply.error = 0;
reply.hdr.cmd = SOF_IPC_GLB_REPLY;
diff --git a/sound/soc/sof/intel/hda-ipc.h b/sound/soc/sof/intel/hda-ipc.h
new file mode 100644
index 000000000000..aef0ceac9803
--- /dev/null
+++ b/sound/soc/sof/intel/hda-ipc.h
@@ -0,0 +1,51 @@
+/* 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 Intel Corporation. All rights reserved.
+ *
+ * Author: Keyon Jie <yang.jie@linux.intel.com>
+ */
+
+#ifndef __SOF_INTEL_HDA_IPC_H
+#define __SOF_INTEL_HDA_IPC_H
+
+/*
+ * Primary register, mapped to
+ * - DIPCTDR (HIPCIDR) in sideband IPC (cAVS 1.8+)
+ * - DIPCT in cAVS 1.5 IPC
+ *
+ * Secondary register, mapped to:
+ * - DIPCTDD (HIPCIDD) in sideband IPC (cAVS 1.8+)
+ * - DIPCTE in cAVS 1.5 IPC
+ */
+
+/* Common bits in primary register */
+
+/* Reserved for doorbell */
+#define HDA_IPC_RSVD_31 BIT(31)
+/* Target, 0 - normal message, 1 - compact message(cAVS compatible) */
+#define HDA_IPC_MSG_COMPACT BIT(30)
+/* Direction, 0 - request, 1 - response */
+#define HDA_IPC_RSP BIT(29)
+
+#define HDA_IPC_TYPE_SHIFT 24
+#define HDA_IPC_TYPE_MASK GENMASK(28, 24)
+#define HDA_IPC_TYPE(x) ((x) << HDA_IPC_TYPE_SHIFT)
+
+#define HDA_IPC_PM_GATE HDA_IPC_TYPE(0x8U)
+
+/* Command specific payload bits in secondary register */
+
+/* Disable DMA tracing (0 - keep tracing, 1 - to disable DMA trace) */
+#define HDA_PM_NO_DMA_TRACE BIT(4)
+/* Prevent clock gating (0 - cg allowed, 1 - DSP clock always on) */
+#define HDA_PM_PCG BIT(3)
+/* Prevent power gating (0 - deep power state transitions allowed) */
+#define HDA_PM_PPG BIT(2)
+/* Indicates whether streaming is active */
+#define HDA_PM_PG_STREAMING BIT(1)
+#define HDA_PM_PG_RSVD BIT(0)
+
+#endif
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 65c2af3fcaab..b1783360fe10 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -126,7 +126,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata,
HDA_DSP_INIT_TIMEOUT_US);
if (ret < 0) {
- dev_err(sdev->dev, "error: waiting for HIPCIE done\n");
+ dev_err(sdev->dev, "error: %s: timeout for HIPCIE done\n",
+ __func__);
goto err;
}
@@ -152,6 +153,10 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata,
if (!ret)
return 0;
+ dev_err(sdev->dev,
+ "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n",
+ __func__);
+
err:
hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX);
hda_dsp_core_reset_power_down(sdev, chip->cores_mask);
@@ -253,10 +258,22 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream)
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_BASEFW_TIMEOUT_US);
+ /*
+ * even in case of errors we still need to stop the DMAs,
+ * but we return the initial error should the DMA stop also fail
+ */
+
+ if (status < 0) {
+ dev_err(sdev->dev,
+ "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n",
+ __func__);
+ }
+
ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_STOP);
if (ret < 0) {
dev_err(sdev->dev, "error: DMA trigger stop failed\n");
- return ret;
+ if (!status)
+ status = ret;
}
return status;
@@ -341,13 +358,15 @@ cleanup:
/*
* Perform codeloader stream cleanup.
* This should be done even if firmware loading fails.
+ * If the cleanup also fails, we return the initial error
*/
ret1 = cl_cleanup(sdev, &sdev->dmab, stream);
if (ret1 < 0) {
dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n");
/* set return value to indicate cleanup failure */
- ret = ret1;
+ if (!ret)
+ ret = ret1;
}
/*
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c
index 9b730f183529..575f5f5877d8 100644
--- a/sound/soc/sof/intel/hda-pcm.c
+++ b/sound/soc/sof/intel/hda-pcm.c
@@ -89,6 +89,7 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream);
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct snd_dma_buffer *dmab;
+ struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
int ret;
u32 size, rate, bits;
@@ -116,9 +117,17 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
/* disable SPIB, to enable buffer wrap for stream */
hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
- /* set host_period_bytes to 0 if no IPC position */
- if (hda && hda->no_ipc_position)
- ipc_params->host_period_bytes = 0;
+ /* update no_stream_position flag for ipc params */
+ if (hda && hda->no_ipc_position) {
+ /* For older ABIs set host_period_bytes to zero to inform
+ * FW we don't want position updates. Newer versions use
+ * no_stream_position for this purpose.
+ */
+ if (v->abi_version < SOF_ABI_VER(3, 10, 0))
+ ipc_params->host_period_bytes = 0;
+ else
+ ipc_params->no_stream_position = 1;
+ }
ipc_params->stream_tag = hstream->stream_tag;
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 0c11fceb28a7..29ab43281670 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -275,8 +275,12 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_STREAM_RUN_TIMEOUT);
- if (ret)
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n",
+ __func__, cmd);
return ret;
+ }
hstream->running = true;
break;
@@ -294,8 +298,12 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_STREAM_RUN_TIMEOUT);
- if (ret)
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n",
+ __func__, cmd);
return ret;
+ }
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset +
SOF_HDA_ADSP_REG_CL_SD_STS,
@@ -356,8 +364,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_STREAM_RUN_TIMEOUT);
- if (ret)
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: %s: timeout on STREAM_SD_OFFSET read1\n",
+ __func__);
return ret;
+ }
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
@@ -418,8 +430,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_STREAM_RUN_TIMEOUT);
- if (ret)
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: %s: timeout on STREAM_SD_OFFSET read2\n",
+ __func__);
return ret;
+ }
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 06e84679087b..91bd88fddac7 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -32,9 +32,6 @@
/* platform specific devices */
#include "shim.h"
-#define IS_CFL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa348)
-#define IS_CNL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9dc8)
-
#define EXCEPT_MAX_HDR_SIZE 0x400
/*
@@ -56,6 +53,11 @@ MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode");
static int hda_dmic_num = -1;
module_param_named(dmic_num, hda_dmic_num, int, 0444);
MODULE_PARM_DESC(dmic_num, "SOF HDA DMIC number");
+
+static bool hda_codec_use_common_hdmi =
+ IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_COMMON_HDMI_CODEC);
+module_param_named(use_common_hdmi, hda_codec_use_common_hdmi, bool, 0444);
+MODULE_PARM_DESC(use_common_hdmi, "SOF HDA use common HDMI codec driver");
#endif
static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = {
@@ -262,12 +264,9 @@ static int hda_init(struct snd_sof_dev *sdev)
/* HDA bus init */
sof_hda_bus_init(bus, &pci->dev);
- /* Workaround for a communication error on CFL (bko#199007) and CNL */
- if (IS_CFL(pci) || IS_CNL(pci))
- bus->polling_mode = 1;
-
bus->use_posbuf = 1;
bus->bdl_pos_adj = 0;
+ bus->sync_write = 1;
mutex_init(&hbus->prepare_mutex);
hbus->pci = pci;
@@ -416,9 +415,16 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
pdata->tplg_filename =
hda_mach->sof_tplg_filename;
- /* firmware: pick the first in machine list */
+ /*
+ * firmware: pick the first in machine list,
+ * or use nocodec firmware name if list is empty
+ */
mach = pdata->desc->machines;
- pdata->fw_filename = mach->sof_fw_filename;
+ if (mach->id[0])
+ pdata->fw_filename = mach->sof_fw_filename;
+ else
+ pdata->fw_filename =
+ pdata->desc->nocodec_fw_filename;
dev_info(bus->dev, "using HDA machine driver %s now\n",
hda_mach->drv_name);
@@ -465,6 +471,7 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
&pdata->machine->mach_params;
mach_params->codec_mask = bus->codec_mask;
mach_params->platform = dev_name(sdev->dev);
+ mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi;
}
/* create codec instances */
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 23e430d3e056..18d7e72bf9b7 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -64,6 +64,13 @@
#define SOF_HDA_PPCTL_PIE BIT(31)
#define SOF_HDA_PPCTL_GPROCEN BIT(30)
+/*Vendor Specific Registers*/
+#define SOF_HDA_VS_D0I3C 0x104A
+
+/* D0I3C Register fields */
+#define SOF_HDA_VS_D0I3C_CIP BIT(0) /* Command-In-Progress */
+#define SOF_HDA_VS_D0I3C_I3 BIT(2) /* D0i3 enable bit */
+
/* DPIB entry size: 8 Bytes = 2 DWords */
#define SOF_HDA_DPIB_ENTRY_SIZE 0x8
@@ -207,6 +214,7 @@
#define HDA_DSP_CTRL_RESET_TIMEOUT 100
#define HDA_DSP_WAIT_TIMEOUT 500 /* 500 msec */
#define HDA_DSP_REG_POLL_INTERVAL_US 500 /* 0.5 msec */
+#define HDA_DSP_REG_POLL_RETRY_COUNT 50
#define HDA_DSP_ADSPIC_IPC 1
#define HDA_DSP_ADSPIS_IPC 1
@@ -304,6 +312,7 @@
#define CNL_DSP_REG_HIPCTDD (CNL_DSP_IPC_BASE + 0x08)
#define CNL_DSP_REG_HIPCIDR (CNL_DSP_IPC_BASE + 0x10)
#define CNL_DSP_REG_HIPCIDA (CNL_DSP_IPC_BASE + 0x14)
+#define CNL_DSP_REG_HIPCIDD (CNL_DSP_IPC_BASE + 0x18)
#define CNL_DSP_REG_HIPCCTL (CNL_DSP_IPC_BASE + 0x28)
/* HIPCI */
@@ -399,6 +408,9 @@ struct sof_intel_hda_dev {
int irq;
+ /* PM related */
+ bool l1_support_changed;/* during suspend, is L1SEN changed or not */
+
/* DMIC device */
struct platform_device *dmic_dev;
};
@@ -455,6 +467,9 @@ int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev,
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);
+
int hda_dsp_suspend(struct snd_sof_dev *sdev);
int hda_dsp_resume(struct snd_sof_dev *sdev);
int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev);
@@ -565,7 +580,9 @@ void hda_codec_jack_check(struct snd_sof_dev *sdev);
#endif /* CONFIG_SND_SOC_SOF_HDA */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) && IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) && \
+ (IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \
+ IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI))
void hda_codec_i915_get(struct snd_sof_dev *sdev);
void hda_codec_i915_put(struct snd_sof_dev *sdev);
@@ -579,7 +596,7 @@ static inline void hda_codec_i915_put(struct snd_sof_dev *sdev) { }
static inline int hda_codec_i915_init(struct snd_sof_dev *sdev) { return 0; }
static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
-#endif /* CONFIG_SND_SOC_SOF_HDA && CONFIG_SND_SOC_HDAC_HDMI */
+#endif
/*
* Trace Control.
@@ -596,7 +613,6 @@ extern struct snd_soc_dai_driver skl_dai[];
*/
extern const struct snd_sof_dsp_ops sof_apl_ops;
extern const struct snd_sof_dsp_ops sof_cnl_ops;
-extern const struct snd_sof_dsp_ops sof_skl_ops;
extern const struct sof_intel_dsp_desc apl_chip_info;
extern const struct sof_intel_dsp_desc cnl_chip_info;
@@ -604,5 +620,6 @@ extern const struct sof_intel_dsp_desc skl_chip_info;
extern const struct sof_intel_dsp_desc icl_chip_info;
extern const struct sof_intel_dsp_desc tgl_chip_info;
extern const struct sof_intel_dsp_desc ehl_chip_info;
+extern const struct sof_intel_dsp_desc jsl_chip_info;
#endif
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index 086eeeab8679..5994e1073364 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -210,9 +210,7 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg,
if (ret == 0) {
dev_err(sdev->dev, "error: ipc timed out for 0x%x size %d\n",
hdr->cmd, hdr->size);
- snd_sof_dsp_dbg_dump(ipc->sdev, SOF_DBG_REGS | SOF_DBG_MBOX);
- snd_sof_ipc_dump(ipc->sdev);
- snd_sof_trace_notify_for_error(ipc->sdev);
+ snd_sof_handle_fw_exception(ipc->sdev);
ret = -ETIMEDOUT;
} else {
/* copy the data returned from DSP */
@@ -796,12 +794,6 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
struct snd_sof_ipc *ipc;
struct snd_sof_ipc_msg *msg;
- /* check if mandatory ops required for ipc are defined */
- if (!sof_ops(sdev)->fw_ready) {
- dev_err(sdev->dev, "error: ipc mandatory ops not defined\n");
- return NULL;
- }
-
ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL);
if (!ipc)
return NULL;
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 824d36fe59fd..93512dcbaacd 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -193,6 +193,16 @@ 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)
+{
+ if (sof_ops(sdev)->set_power_state)
+ return sof_ops(sdev)->set_power_state(sdev, substate);
+
+ /* D0 substate is not supported */
+ return -ENOTSUPP;
+}
+
/* debug */
static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags)
{
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 2b876d497447..3d5cd1b445ba 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -19,12 +19,11 @@
#define DRV_NAME "sof-audio-component"
/* Create DMA buffer page table for DSP */
-static int create_page_table(struct snd_pcm_substream *substream,
+static int create_page_table(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
unsigned char *dma_area, size_t size)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
@@ -95,13 +94,12 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
/* this may get called several times by oss emulation */
-static int sof_pcm_hw_params(struct snd_pcm_substream *substream,
+static int sof_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
struct sof_ipc_pcm_params pcm;
@@ -135,7 +133,7 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream,
* ret == 0 means the buffer is not changed
* so no need to regenerate the page table
*/
- ret = create_page_table(substream, runtime->dma_area,
+ ret = create_page_table(component, substream, runtime->dma_area,
runtime->dma_bytes);
if (ret < 0)
return ret;
@@ -237,11 +235,10 @@ static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream,
return ret;
}
-static int sof_pcm_hw_free(struct snd_pcm_substream *substream)
+static int sof_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
int ret, err = 0;
@@ -276,11 +273,10 @@ static int sof_pcm_hw_free(struct snd_pcm_substream *substream)
return err;
}
-static int sof_pcm_prepare(struct snd_pcm_substream *substream)
+static int sof_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
int ret;
@@ -300,7 +296,8 @@ static int sof_pcm_prepare(struct snd_pcm_substream *substream)
substream->stream);
/* set hw_params */
- ret = sof_pcm_hw_params(substream, &spcm->params[substream->stream]);
+ ret = sof_pcm_hw_params(component,
+ substream, &spcm->params[substream->stream]);
if (ret < 0) {
dev_err(sdev->dev, "error: set pcm hw_params after resume\n");
return ret;
@@ -313,11 +310,10 @@ static int sof_pcm_prepare(struct snd_pcm_substream *substream)
* FE dai link trigger actions are always executed in non-atomic context because
* they involve IPC's.
*/
-static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int sof_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
struct sof_ipc_stream stream;
@@ -350,8 +346,18 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
break;
case SNDRV_PCM_TRIGGER_RESUME:
+ if (spcm->stream[substream->stream].suspend_ignored) {
+ /*
+ * this case will be triggered when INFO_RESUME is
+ * supported, no need to resume streams that remained
+ * enabled in D0ix.
+ */
+ spcm->stream[substream->stream].suspend_ignored = false;
+ return 0;
+ }
+
/* set up hw_params */
- ret = sof_pcm_prepare(substream);
+ ret = sof_pcm_prepare(component, substream);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to set up hw_params upon resume\n");
@@ -360,9 +366,30 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
/* fallthrough */
case SNDRV_PCM_TRIGGER_START:
+ if (spcm->stream[substream->stream].suspend_ignored) {
+ /*
+ * This case will be triggered when INFO_RESUME is
+ * not supported, no need to re-start streams that
+ * remained enabled in D0ix.
+ */
+ spcm->stream[substream->stream].suspend_ignored = false;
+ return 0;
+ }
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (sdev->s0_suspend &&
+ spcm->stream[substream->stream].d0i3_compatible) {
+ /*
+ * trap the event, not sending trigger stop to
+ * prevent the FW pipelines from being stopped,
+ * and mark the flag to ignore the upcoming DAPM
+ * PM events.
+ */
+ spcm->stream[substream->stream].suspend_ignored = true;
+ return 0;
+ }
+ /* fallthrough */
case SNDRV_PCM_TRIGGER_STOP:
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
ipc_first = true;
@@ -395,11 +422,10 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
-static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
snd_pcm_uframes_t host, dai;
@@ -428,13 +454,24 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream)
return host;
}
-static int sof_pcm_open(struct snd_pcm_substream *substream)
+#ifdef CONFIG_SND_DMA_SGBUF
+static struct page *sof_pcm_page(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return snd_pcm_sgbuf_ops_page(substream, offset);
+}
+#else
+#define sof_pcm_page NULL
+#endif /* CONFIG_SND_DMA_SGBUF */
+
+static int sof_pcm_open(struct snd_soc_component *component,
+ 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 =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ const struct snd_sof_dsp_ops *ops = sof_ops(sdev);
struct snd_sof_pcm *spcm;
struct snd_soc_tplg_stream_caps *caps;
int ret;
@@ -464,11 +501,8 @@ static int sof_pcm_open(struct snd_pcm_substream *substream)
le32_to_cpu(caps->period_size_min));
/* set runtime config */
- runtime->hw.info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP;
+ runtime->hw.info = ops->hw_info; /* platform-specific */
+
runtime->hw.formats = le64_to_cpu(caps->formats);
runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min);
runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max);
@@ -505,11 +539,10 @@ static int sof_pcm_open(struct snd_pcm_substream *substream)
return ret;
}
-static int sof_pcm_close(struct snd_pcm_substream *substream)
+static int sof_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
int err;
@@ -538,27 +571,14 @@ static int sof_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops sof_pcm_ops = {
- .open = sof_pcm_open,
- .close = sof_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = sof_pcm_hw_params,
- .prepare = sof_pcm_prepare,
- .hw_free = sof_pcm_hw_free,
- .trigger = sof_pcm_trigger,
- .pointer = sof_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
-};
-
/*
* Pre-allocate playback/capture audio buffer pages.
* no need to explicitly release memory preallocated by sof_pcm_new in pcm_free
* snd_pcm_lib_preallocate_free_for_all() is called by the core.
*/
-static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int sof_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
struct snd_pcm *pcm = rtd->pcm;
@@ -691,6 +711,14 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
case SOF_DAI_INTEL_ALH:
/* do nothing for ALH dai_link */
break;
+ case SOF_DAI_IMX_ESAI:
+ channels->min = dai->dai_config->esai.tdm_slots;
+ channels->max = dai->dai_config->esai.tdm_slots;
+
+ dev_dbg(sdev->dev,
+ "channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
default:
dev_err(sdev->dev, "error: invalid DAI type %d\n",
dai->dai_config->type);
@@ -752,11 +780,20 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->name = "sof-audio-component";
pd->probe = sof_pcm_probe;
pd->remove = sof_pcm_remove;
- pd->ops = &sof_pcm_ops;
+ pd->open = sof_pcm_open;
+ pd->close = sof_pcm_close;
+ pd->ioctl = snd_soc_pcm_lib_ioctl;
+ pd->hw_params = sof_pcm_hw_params;
+ pd->prepare = sof_pcm_prepare;
+ pd->hw_free = sof_pcm_hw_free;
+ pd->trigger = sof_pcm_trigger;
+ pd->pointer = sof_pcm_pointer;
+ pd->page = sof_pcm_page;
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
pd->compr_ops = &sof_compressed_ops;
#endif
- pd->pcm_new = sof_pcm_new;
+ pd->pcm_construct = sof_pcm_new;
pd->ignore_machine = drv_name;
pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
pd->be_pcm_base = SOF_BE_PCM_BASE;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index e23beaeefe00..0fd5567237a8 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -197,7 +197,7 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev)
return ret;
}
-static int sof_send_pm_ipc(struct snd_sof_dev *sdev, int cmd)
+static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
{
struct sof_ipc_pm_ctx pm_ctx;
struct sof_ipc_reply reply;
@@ -320,12 +320,15 @@ static int sof_resume(struct device *dev, bool runtime_resume)
}
/* notify DSP of system resume */
- ret = sof_send_pm_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
+ ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
if (ret < 0)
dev_err(sdev->dev,
"error: ctx_restore ipc error during resume %d\n",
ret);
+ /* initialize default D0 sub-state */
+ sdev->d0_substate = SOF_DSP_D0I0;
+
return ret;
}
@@ -358,7 +361,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
sof_cache_debugfs(sdev);
#endif
/* notify DSP of upcoming power down */
- ret = sof_send_pm_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
+ ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
if (ret == -EBUSY || ret == -EAGAIN) {
/*
* runtime PM has logic to handle -EBUSY/-EAGAIN so
@@ -408,14 +411,135 @@ 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);
+
+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;
+#else
+ /* will suspend to S3 by default */
+ sdev->s0_suspend = false;
+#endif
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_sof_prepare);
+
+void snd_sof_complete(struct device *dev)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+
+ sdev->s0_suspend = false;
+}
+EXPORT_SYMBOL(snd_sof_complete);
diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c
index ea7b8b895412..df318f50dd0b 100644
--- a/sound/soc/sof/sof-acpi-dev.c
+++ b/sound/soc/sof/sof-acpi-dev.c
@@ -29,6 +29,12 @@ static char *tplg_path;
module_param(tplg_path, charp, 0444);
MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology.");
+static int sof_acpi_debug;
+module_param_named(sof_acpi_debug, sof_acpi_debug, int, 0444);
+MODULE_PARM_DESC(sof_acpi_debug, "SOF ACPI debug options (0x0 all off)");
+
+#define SOF_ACPI_DISABLE_PM_RUNTIME BIT(0)
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL)
static const struct sof_dev_desc sof_acpi_haswell_desc = {
.machines = snd_soc_acpi_intel_haswell_machines,
@@ -121,6 +127,9 @@ static const struct dev_pm_ops sof_acpi_pm = {
static void sof_acpi_probe_complete(struct device *dev)
{
+ if (sof_acpi_debug & SOF_ACPI_DISABLE_PM_RUNTIME)
+ return;
+
/* allow runtime_pm */
pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
@@ -221,7 +230,8 @@ static int sof_acpi_probe(struct platform_device *pdev)
static int sof_acpi_remove(struct platform_device *pdev)
{
- pm_runtime_disable(&pdev->dev);
+ if (!(sof_acpi_debug & SOF_ACPI_DISABLE_PM_RUNTIME))
+ pm_runtime_disable(&pdev->dev);
/* call sof helper for DSP hardware remove */
snd_sof_device_remove(&pdev->dev);
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c
index d66412a77873..3252dbe277c8 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -29,6 +29,12 @@ static char *tplg_path;
module_param(tplg_path, charp, 0444);
MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology.");
+static int sof_pci_debug;
+module_param_named(sof_pci_debug, sof_pci_debug, int, 0444);
+MODULE_PARM_DESC(sof_pci_debug, "SOF PCI debug options (0x0 all off)");
+
+#define SOF_PCI_DISABLE_PM_RUNTIME BIT(0)
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
static const struct sof_dev_desc bxt_desc = {
.machines = snd_soc_acpi_intel_bxt_machines,
@@ -113,7 +119,7 @@ static const struct sof_dev_desc cnl_desc = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
static const struct sof_dev_desc cfl_desc = {
- .machines = snd_soc_acpi_intel_cnl_machines,
+ .machines = snd_soc_acpi_intel_cfl_machines,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
@@ -122,7 +128,7 @@ static const struct sof_dev_desc cfl_desc = {
.chip_info = &cnl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
- .nocodec_fw_filename = "sof-cnl.ri",
+ .nocodec_fw_filename = "sof-cfl.ri",
.nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
.ops = &sof_cnl_ops,
.arch_ops = &sof_xtensa_arch_ops
@@ -133,7 +139,7 @@ static const struct sof_dev_desc cfl_desc = {
IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H)
static const struct sof_dev_desc cml_desc = {
- .machines = snd_soc_acpi_intel_cnl_machines,
+ .machines = snd_soc_acpi_intel_cml_machines,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
@@ -142,7 +148,7 @@ static const struct sof_dev_desc cml_desc = {
.chip_info = &cnl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
- .nocodec_fw_filename = "sof-cnl.ri",
+ .nocodec_fw_filename = "sof-cml.ri",
.nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
.ops = &sof_cnl_ops,
.arch_ops = &sof_xtensa_arch_ops
@@ -167,42 +173,6 @@ static const struct sof_dev_desc icl_desc = {
};
#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE)
-static const struct sof_dev_desc skl_desc = {
- .machines = snd_soc_acpi_intel_skl_machines,
- .resindex_lpe_base = 0,
- .resindex_pcicfg_base = -1,
- .resindex_imr_base = -1,
- .irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
- .chip_info = &skl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .nocodec_fw_filename = "sof-skl.ri",
- .nocodec_tplg_filename = "sof-skl-nocodec.tplg",
- .ops = &sof_skl_ops,
- .arch_ops = &sof_xtensa_arch_ops
-};
-#endif
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE)
-static const struct sof_dev_desc kbl_desc = {
- .machines = snd_soc_acpi_intel_kbl_machines,
- .resindex_lpe_base = 0,
- .resindex_pcicfg_base = -1,
- .resindex_imr_base = -1,
- .irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
- .chip_info = &skl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .nocodec_fw_filename = "sof-kbl.ri",
- .nocodec_tplg_filename = "sof-kbl-nocodec.tplg",
- .ops = &sof_skl_ops,
- .arch_ops = &sof_xtensa_arch_ops
-};
-#endif
-
#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
static const struct sof_dev_desc tgl_desc = {
.machines = snd_soc_acpi_intel_tgl_machines,
@@ -239,7 +209,27 @@ static const struct sof_dev_desc ehl_desc = {
};
#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
+static const struct sof_dev_desc jsl_desc = {
+ .machines = snd_soc_acpi_intel_jsl_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .resindex_dma_base = -1,
+ .chip_info = &jsl_chip_info,
+ .default_fw_path = "intel/sof",
+ .default_tplg_path = "intel/sof-tplg",
+ .nocodec_fw_filename = "sof-jsl.ri",
+ .nocodec_tplg_filename = "sof-jsl-nocodec.tplg",
+ .ops = &sof_cnl_ops,
+ .arch_ops = &sof_xtensa_arch_ops
+};
+#endif
+
static const struct dev_pm_ops sof_pci_pm = {
+ .prepare = snd_sof_prepare,
+ .complete = snd_sof_complete,
SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume)
SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume,
snd_sof_runtime_idle)
@@ -249,6 +239,9 @@ static void sof_pci_probe_complete(struct device *dev)
{
dev_dbg(dev, "Completing SOF PCI probe");
+ if (sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME)
+ return;
+
/* allow runtime_pm */
pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
@@ -370,7 +363,8 @@ static void sof_pci_remove(struct pci_dev *pci)
snd_sof_device_remove(&pci->dev);
/* follow recommendation in pci-driver.c to increment usage counter */
- pm_runtime_get_noresume(&pci->dev);
+ if (!(sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME))
+ pm_runtime_get_noresume(&pci->dev);
/* release pci regions and disable device */
pci_release_regions(pci);
@@ -401,18 +395,14 @@ static const struct pci_device_id sof_pci_ids[] = {
{ PCI_DEVICE(0x8086, 0xa348),
.driver_data = (unsigned long)&cfl_desc},
#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE)
- { PCI_DEVICE(0x8086, 0x9d71),
- .driver_data = (unsigned long)&kbl_desc},
-#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE)
- { PCI_DEVICE(0x8086, 0x9d70),
- .driver_data = (unsigned long)&skl_desc},
-#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
{ PCI_DEVICE(0x8086, 0x34C8),
.driver_data = (unsigned long)&icl_desc},
#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
+ { PCI_DEVICE(0x8086, 0x38c8),
+ .driver_data = (unsigned long)&jsl_desc},
+#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_LP)
{ PCI_DEVICE(0x8086, 0x02c8),
.driver_data = (unsigned long)&cml_desc},
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 730f3259dd02..c7c2c70ee4d0 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -15,6 +15,7 @@
#include <sound/hdaudio.h>
#include <sound/soc.h>
+#include <sound/control.h>
#include <sound/sof.h>
#include <sound/sof/stream.h> /* needs to be included before control.h */
@@ -28,10 +29,15 @@
#include <uapi/sound/sof/fw.h>
/* debug flags */
-#define SOF_DBG_REGS BIT(1)
-#define SOF_DBG_MBOX BIT(2)
-#define SOF_DBG_TEXT BIT(3)
-#define SOF_DBG_PCI BIT(4)
+#define SOF_DBG_ENABLE_TRACE BIT(0)
+#define SOF_DBG_REGS BIT(1)
+#define SOF_DBG_MBOX BIT(2)
+#define SOF_DBG_TEXT BIT(3)
+#define SOF_DBG_PCI BIT(4)
+#define SOF_DBG_RETAIN_CTX BIT(5) /* prevent DSP D3 on FW exception */
+
+/* global debug state set by SOF_DBG_ flags */
+extern int sof_core_debug;
/* max BARs mmaped devices can use */
#define SND_SOF_BARS 8
@@ -62,6 +68,12 @@
#define DMA_CHAN_INVALID 0xFFFFFFFF
+/* DSP D0ix sub-state */
+enum sof_d0_substate {
+ SOF_DSP_D0I0 = 0, /* DSP default D0 substate */
+ SOF_DSP_D0I3, /* DSP D0i3(low power) substate*/
+};
+
struct snd_sof_dev;
struct snd_sof_ipc_msg;
struct snd_sof_ipc;
@@ -128,7 +140,7 @@ struct snd_sof_dsp_ops {
* FW ready checks for ABI compatibility and creates
* memory windows at first boot
*/
- int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); /* optional */
+ int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); /* mandatory */
/* connect pcm substream to a host stream */
int (*pcm_open)(struct snd_sof_dev *sdev,
@@ -177,6 +189,8 @@ struct snd_sof_dsp_ops {
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 */
/* DSP clocking */
int (*set_clk)(struct snd_sof_dev *sof_dev, u32 freq); /* optional */
@@ -205,6 +219,9 @@ struct snd_sof_dsp_ops {
/* DAI ops */
struct snd_soc_dai_driver *drv;
int num_drv;
+
+ /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */
+ u32 hw_info;
};
/* DSP architecture specific callbacks for oops and stack dumps */
@@ -293,6 +310,12 @@ struct snd_sof_pcm_stream {
struct sof_ipc_stream_posn posn;
struct snd_pcm_substream *substream;
struct work_struct period_elapsed_work;
+ bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */
+ /*
+ * flag to indicate that the DSP pipelines should be kept
+ * active or not while suspending the stream
+ */
+ bool suspend_ignored;
};
/* ALSA SOF PCM device */
@@ -305,6 +328,12 @@ struct snd_sof_pcm {
bool prepared[2]; /* PCM_PARAMS set successfully */
};
+struct snd_sof_led_control {
+ unsigned int use_led;
+ unsigned int direction;
+ unsigned int led_value;
+};
+
/* ALSA SOF Kcontrol device */
struct snd_sof_control {
struct snd_sof_dev *sdev;
@@ -319,6 +348,8 @@ struct snd_sof_control {
u32 *volume_table; /* volume table computed from tlv data*/
struct list_head list; /* list in sdev control list */
+
+ struct snd_sof_led_control led_ctl;
};
/* ASoC SOF DAPM widget */
@@ -370,6 +401,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;
+
/* DSP firmware boot */
wait_queue_head_t boot_wait;
u32 boot_complete;
@@ -434,6 +470,7 @@ struct snd_sof_dev {
int dma_trace_pages;
wait_queue_head_t trace_sleep;
u32 host_offset;
+ u32 dtrace_is_supported; /* set with Kconfig or module parameter */
u32 dtrace_is_enabled;
u32 dtrace_error;
u32 dtrace_draining;
@@ -455,6 +492,10 @@ int snd_sof_runtime_resume(struct device *dev);
int snd_sof_runtime_idle(struct device *dev);
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);
@@ -512,6 +553,8 @@ struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev,
return NULL;
}
+bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev);
+
struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev,
const char *name);
struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev,
@@ -575,6 +618,7 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code,
struct sof_ipc_panic_info *panic_info,
void *stack, size_t stack_words);
int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev);
+void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev);
/*
* Platform specific ops.
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 4452594c2e17..d82ab981e840 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -135,7 +135,9 @@ static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
struct snd_sof_widget *swidget = w->dobj.private;
+ int stream = SNDRV_PCM_STREAM_CAPTURE;
struct snd_sof_dev *sdev;
+ struct snd_sof_pcm *spcm;
int ret = 0;
if (!swidget)
@@ -146,11 +148,24 @@ static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w,
dev_dbg(sdev->dev, "received event %d for widget %s\n",
event, w->name);
+ /* get runtime PCM params using widget's stream name */
+ spcm = snd_sof_find_spcm_name(sdev, swidget->widget->sname);
+ if (!spcm) {
+ dev_err(sdev->dev, "error: cannot find PCM for %s\n",
+ swidget->widget->name);
+ return -EINVAL;
+ }
+
/* process events */
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ if (spcm->stream[stream].suspend_ignored) {
+ dev_dbg(sdev->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n");
+ return 0;
+ }
+
/* set pcm params */
- ret = ipc_pcm_params(swidget, SOF_IPC_STREAM_CAPTURE);
+ ret = ipc_pcm_params(swidget, stream);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to set pcm params for widget %s\n",
@@ -166,6 +181,11 @@ static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w,
swidget->widget->name);
break;
case SND_SOC_DAPM_POST_PMD:
+ if (spcm->stream[stream].suspend_ignored) {
+ dev_dbg(sdev->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n");
+ return 0;
+ }
+
/* stop trigger */
ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP);
if (ret < 0)
@@ -433,164 +453,6 @@ static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type)
}
/*
- * Standard Kcontrols.
- */
-
-static int sof_control_load_volume(struct snd_soc_component *scomp,
- struct snd_sof_control *scontrol,
- struct snd_kcontrol_new *kc,
- struct snd_soc_tplg_ctl_hdr *hdr)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_mixer_control *mc =
- container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
- struct sof_ipc_ctrl_data *cdata;
- int tlv[TLV_ITEMS];
- unsigned int i;
- int ret;
-
- /* validate topology data */
- if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN)
- return -EINVAL;
-
- /* init the volume get/put data */
- scontrol->size = struct_size(scontrol->control_data, chanv,
- le32_to_cpu(mc->num_channels));
- scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
- if (!scontrol->control_data)
- return -ENOMEM;
-
- scontrol->comp_id = sdev->next_comp_id;
- scontrol->min_volume_step = le32_to_cpu(mc->min);
- scontrol->max_volume_step = le32_to_cpu(mc->max);
- scontrol->num_channels = le32_to_cpu(mc->num_channels);
-
- /* set cmd for mixer control */
- if (le32_to_cpu(mc->max) == 1) {
- scontrol->cmd = SOF_CTRL_CMD_SWITCH;
- goto out;
- }
-
- scontrol->cmd = SOF_CTRL_CMD_VOLUME;
-
- /* extract tlv data */
- if (get_tlv_data(kc->tlv.p, tlv) < 0) {
- dev_err(sdev->dev, "error: invalid TLV data\n");
- return -EINVAL;
- }
-
- /* set up volume table */
- ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1);
- if (ret < 0) {
- dev_err(sdev->dev, "error: setting up volume table\n");
- return ret;
- }
-
- /* set default volume values to 0dB in control */
- cdata = scontrol->control_data;
- for (i = 0; i < scontrol->num_channels; i++) {
- cdata->chanv[i].channel = i;
- cdata->chanv[i].value = VOL_ZERO_DB;
- }
-
-out:
- dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n",
- scontrol->comp_id, scontrol->num_channels);
-
- return 0;
-}
-
-static int sof_control_load_enum(struct snd_soc_component *scomp,
- struct snd_sof_control *scontrol,
- struct snd_kcontrol_new *kc,
- struct snd_soc_tplg_ctl_hdr *hdr)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_enum_control *ec =
- container_of(hdr, struct snd_soc_tplg_enum_control, hdr);
-
- /* validate topology data */
- if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN)
- return -EINVAL;
-
- /* init the enum get/put data */
- scontrol->size = struct_size(scontrol->control_data, chanv,
- le32_to_cpu(ec->num_channels));
- scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
- if (!scontrol->control_data)
- return -ENOMEM;
-
- scontrol->comp_id = sdev->next_comp_id;
- scontrol->num_channels = le32_to_cpu(ec->num_channels);
-
- scontrol->cmd = SOF_CTRL_CMD_ENUM;
-
- dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n",
- scontrol->comp_id, scontrol->num_channels, scontrol->comp_id);
-
- return 0;
-}
-
-static int sof_control_load_bytes(struct snd_soc_component *scomp,
- struct snd_sof_control *scontrol,
- struct snd_kcontrol_new *kc,
- struct snd_soc_tplg_ctl_hdr *hdr)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc_ctrl_data *cdata;
- struct snd_soc_tplg_bytes_control *control =
- container_of(hdr, struct snd_soc_tplg_bytes_control, hdr);
- struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value;
- int max_size = sbe->max;
-
- /* init the get/put bytes data */
- scontrol->size = sizeof(struct sof_ipc_ctrl_data) +
- le32_to_cpu(control->priv.size);
-
- if (scontrol->size > max_size) {
- dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n",
- scontrol->size, max_size);
- return -EINVAL;
- }
-
- scontrol->control_data = kzalloc(max_size, GFP_KERNEL);
- cdata = scontrol->control_data;
- if (!scontrol->control_data)
- return -ENOMEM;
-
- scontrol->comp_id = sdev->next_comp_id;
- scontrol->cmd = SOF_CTRL_CMD_BINARY;
-
- dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n",
- scontrol->comp_id, scontrol->num_channels);
-
- if (le32_to_cpu(control->priv.size) > 0) {
- memcpy(cdata->data, control->priv.data,
- le32_to_cpu(control->priv.size));
-
- if (cdata->data->magic != SOF_ABI_MAGIC) {
- dev_err(sdev->dev, "error: Wrong ABI magic 0x%08x.\n",
- cdata->data->magic);
- return -EINVAL;
- }
- if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION,
- cdata->data->abi)) {
- dev_err(sdev->dev,
- "error: Incompatible ABI version 0x%08x.\n",
- cdata->data->abi);
- return -EINVAL;
- }
- if (cdata->data->size + sizeof(const struct sof_abi_hdr) !=
- le32_to_cpu(control->priv.size)) {
- dev_err(sdev->dev,
- "error: Conflict in bytes vs. priv size.\n");
- return -EINVAL;
- }
- }
- return 0;
-}
-
-/*
* Topology Token Parsing.
* New tokens should be added to headers and parsing tables below.
*/
@@ -725,6 +587,16 @@ static const struct sof_topology_token pcm_tokens[] = {
offsetof(struct sof_ipc_comp_host, dmac_config), 0},
};
+/* PCM */
+static const struct sof_topology_token stream_tokens[] = {
+ {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3,
+ SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible), 0},
+ {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3,
+ SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible), 0},
+};
+
/* Generic components */
static const struct sof_topology_token comp_tokens[] = {
{SOF_TKN_COMP_PERIOD_SINK_COUNT,
@@ -799,6 +671,13 @@ static const struct sof_topology_token dmic_tokens[] = {
};
+/* ESAI */
+static const struct sof_topology_token esai_tokens[] = {
+ {SOF_TKN_IMX_ESAI_MCLK_ID,
+ SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_esai_params, mclk_id), 0},
+};
+
/*
* DMIC PDM Tokens
* SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token
@@ -840,6 +719,14 @@ static const struct sof_topology_token dmic_pdm_tokens[] = {
static const struct sof_topology_token hda_tokens[] = {
};
+/* Leds */
+static const struct sof_topology_token led_tokens[] = {
+ {SOF_TKN_MUTE_LED_USE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_led_control, use_led), 0},
+ {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ get_token_u32, offsetof(struct snd_sof_led_control, direction), 0},
+};
+
static void sof_parse_uuid_tokens(struct snd_soc_component *scomp,
void *object,
const struct sof_topology_token *tokens,
@@ -1040,6 +927,200 @@ static void sof_dbg_comp_config(struct snd_soc_component *scomp,
config->frame_fmt);
}
+/*
+ * Standard Kcontrols.
+ */
+
+static int sof_control_load_volume(struct snd_soc_component *scomp,
+ struct snd_sof_control *scontrol,
+ struct snd_kcontrol_new *kc,
+ struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct snd_soc_tplg_mixer_control *mc =
+ container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
+ struct sof_ipc_ctrl_data *cdata;
+ int tlv[TLV_ITEMS];
+ unsigned int i;
+ int ret = 0;
+
+ /* validate topology data */
+ if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* init the volume get/put data */
+ scontrol->size = struct_size(scontrol->control_data, chanv,
+ le32_to_cpu(mc->num_channels));
+ scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
+ if (!scontrol->control_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ scontrol->comp_id = sdev->next_comp_id;
+ scontrol->min_volume_step = le32_to_cpu(mc->min);
+ scontrol->max_volume_step = le32_to_cpu(mc->max);
+ scontrol->num_channels = le32_to_cpu(mc->num_channels);
+
+ /* set cmd for mixer control */
+ if (le32_to_cpu(mc->max) == 1) {
+ scontrol->cmd = SOF_CTRL_CMD_SWITCH;
+ goto skip;
+ }
+
+ scontrol->cmd = SOF_CTRL_CMD_VOLUME;
+
+ /* extract tlv data */
+ if (get_tlv_data(kc->tlv.p, tlv) < 0) {
+ dev_err(sdev->dev, "error: invalid TLV data\n");
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ /* set up volume table */
+ ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: setting up volume table\n");
+ goto out_free;
+ }
+
+ /* set default volume values to 0dB in control */
+ cdata = scontrol->control_data;
+ for (i = 0; i < scontrol->num_channels; i++) {
+ cdata->chanv[i].channel = i;
+ cdata->chanv[i].value = VOL_ZERO_DB;
+ }
+
+skip:
+ /* set up possible led control from mixer private data */
+ ret = sof_parse_tokens(scomp, &scontrol->led_ctl, led_tokens,
+ ARRAY_SIZE(led_tokens), mc->priv.array,
+ le32_to_cpu(mc->priv.size));
+ if (ret != 0) {
+ dev_err(sdev->dev, "error: parse led tokens failed %d\n",
+ le32_to_cpu(mc->priv.size));
+ goto out_free_table;
+ }
+
+ dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n",
+ scontrol->comp_id, scontrol->num_channels);
+
+ return ret;
+
+out_free_table:
+ if (le32_to_cpu(mc->max) > 1)
+ kfree(scontrol->volume_table);
+out_free:
+ kfree(scontrol->control_data);
+out:
+ return ret;
+}
+
+static int sof_control_load_enum(struct snd_soc_component *scomp,
+ struct snd_sof_control *scontrol,
+ struct snd_kcontrol_new *kc,
+ struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct snd_soc_tplg_enum_control *ec =
+ container_of(hdr, struct snd_soc_tplg_enum_control, hdr);
+
+ /* validate topology data */
+ if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN)
+ return -EINVAL;
+
+ /* init the enum get/put data */
+ scontrol->size = struct_size(scontrol->control_data, chanv,
+ le32_to_cpu(ec->num_channels));
+ scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
+ if (!scontrol->control_data)
+ return -ENOMEM;
+
+ scontrol->comp_id = sdev->next_comp_id;
+ scontrol->num_channels = le32_to_cpu(ec->num_channels);
+
+ scontrol->cmd = SOF_CTRL_CMD_ENUM;
+
+ dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n",
+ scontrol->comp_id, scontrol->num_channels, scontrol->comp_id);
+
+ return 0;
+}
+
+static int sof_control_load_bytes(struct snd_soc_component *scomp,
+ struct snd_sof_control *scontrol,
+ struct snd_kcontrol_new *kc,
+ struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct sof_ipc_ctrl_data *cdata;
+ struct snd_soc_tplg_bytes_control *control =
+ container_of(hdr, struct snd_soc_tplg_bytes_control, hdr);
+ struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value;
+ int max_size = sbe->max;
+ int ret = 0;
+
+ /* init the get/put bytes data */
+ scontrol->size = sizeof(struct sof_ipc_ctrl_data) +
+ le32_to_cpu(control->priv.size);
+
+ if (scontrol->size > max_size) {
+ dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n",
+ scontrol->size, max_size);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ scontrol->control_data = kzalloc(max_size, GFP_KERNEL);
+ cdata = scontrol->control_data;
+ if (!scontrol->control_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ scontrol->comp_id = sdev->next_comp_id;
+ scontrol->cmd = SOF_CTRL_CMD_BINARY;
+
+ dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n",
+ scontrol->comp_id, scontrol->num_channels);
+
+ if (le32_to_cpu(control->priv.size) > 0) {
+ memcpy(cdata->data, control->priv.data,
+ le32_to_cpu(control->priv.size));
+
+ if (cdata->data->magic != SOF_ABI_MAGIC) {
+ dev_err(sdev->dev, "error: Wrong ABI magic 0x%08x.\n",
+ cdata->data->magic);
+ ret = -EINVAL;
+ goto out_free;
+ }
+ if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION,
+ cdata->data->abi)) {
+ dev_err(sdev->dev,
+ "error: Incompatible ABI version 0x%08x.\n",
+ cdata->data->abi);
+ ret = -EINVAL;
+ goto out_free;
+ }
+ if (cdata->data->size + sizeof(const struct sof_abi_hdr) !=
+ le32_to_cpu(control->priv.size)) {
+ dev_err(sdev->dev,
+ "error: Conflict in bytes vs. priv size.\n");
+ ret = -EINVAL;
+ goto out_free;
+ }
+ }
+
+ return ret;
+
+out_free:
+ kfree(scontrol->control_data);
+out:
+ return ret;
+}
+
/* external kcontrol init - used for any driver specific init */
static int sof_control_load(struct snd_soc_component *scomp, int index,
struct snd_kcontrol_new *kc,
@@ -1095,6 +1176,11 @@ static int sof_control_load(struct snd_soc_component *scomp, int index,
return 0;
}
+ if (ret < 0) {
+ kfree(scontrol);
+ return ret;
+ }
+
dobj->private = scontrol;
list_add(&scontrol->list, &sdev->kcontrol_list);
return ret;
@@ -1581,7 +1667,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index,
if (!volume)
return -ENOMEM;
- if (le32_to_cpu(tw->num_kcontrols) != 1) {
+ if (!le32_to_cpu(tw->num_kcontrols)) {
dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n",
tw->num_kcontrols);
ret = -EINVAL;
@@ -1618,7 +1704,8 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index,
swidget->private = volume;
list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
- if (scontrol->comp_id == swidget->comp_id) {
+ if (scontrol->comp_id == swidget->comp_id &&
+ scontrol->volume_table) {
min_step = scontrol->min_volume_step;
max_step = scontrol->max_volume_step;
volume->min_value = scontrol->volume_table[min_step];
@@ -2272,6 +2359,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_stream_caps *caps;
+ struct snd_soc_tplg_private *private = &pcm->priv;
struct snd_sof_pcm *spcm;
int stream = SNDRV_PCM_STREAM_PLAYBACK;
int ret = 0;
@@ -2288,17 +2376,28 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = COMP_ID_UNASSIGNED;
spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = COMP_ID_UNASSIGNED;
- if (pcm) {
- spcm->pcm = *pcm;
- dev_dbg(sdev->dev, "tplg: load pcm %s\n", pcm->dai_name);
- }
+ spcm->pcm = *pcm;
+ dev_dbg(sdev->dev, "tplg: load pcm %s\n", pcm->dai_name);
+
dai_drv->dobj.private = spcm;
list_add(&spcm->list, &sdev->pcm_list);
+ ret = sof_parse_tokens(scomp, spcm, stream_tokens,
+ ARRAY_SIZE(stream_tokens), private->array,
+ le32_to_cpu(private->size));
+ if (ret) {
+ dev_err(sdev->dev, "error: parse stream tokens failed %d\n",
+ le32_to_cpu(private->size));
+ return ret;
+ }
+
/* do we need to allocate playback PCM DMA pages */
if (!spcm->pcm.playback)
goto capture;
+ dev_vdbg(sdev->dev, "tplg: pcm %s stream tokens: playback d0i3:%d\n",
+ spcm->pcm.pcm_name, spcm->stream[0].d0i3_compatible);
+
caps = &spcm->pcm.caps[stream];
/* allocate playback page table buffer */
@@ -2326,6 +2425,9 @@ capture:
if (!spcm->pcm.capture)
return ret;
+ dev_vdbg(sdev->dev, "tplg: pcm %s stream tokens: capture d0i3:%d\n",
+ spcm->pcm.pcm_name, spcm->stream[1].d0i3_compatible);
+
caps = &spcm->pcm.caps[stream];
/* allocate capture page table buffer */
@@ -2536,8 +2638,66 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_hw_config *hw_config,
struct sof_ipc_dai_config *config)
{
- /*TODO: Add implementation */
- return 0;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct snd_soc_tplg_private *private = &cfg->priv;
+ struct sof_ipc_reply reply;
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->esai, 0, sizeof(struct sof_ipc_dai_esai_params));
+ config->hdr.size = size;
+
+ ret = sof_parse_tokens(scomp, &config->esai, esai_tokens,
+ ARRAY_SIZE(esai_tokens), private->array,
+ le32_to_cpu(private->size));
+ if (ret != 0) {
+ dev_err(sdev->dev, "error: parse esai tokens failed %d\n",
+ le32_to_cpu(private->size));
+ return ret;
+ }
+
+ config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate);
+ config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate);
+ config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->esai.mclk_direction = hw_config->mclk_direction;
+ config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+ config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);
+ config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots);
+ config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots);
+
+ dev_info(sdev->dev,
+ "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",
+ config->dai_index, config->format,
+ config->esai.mclk_rate, config->esai.tdm_slot_width,
+ config->esai.tdm_slots, config->esai.mclk_id);
+
+ if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) {
+ dev_err(sdev->dev, "error: invalid channel count for ESAI%d\n",
+ config->dai_index);
+ return -EINVAL;
+ }
+
+ /* send message to DSP */
+ ret = sof_ipc_tx_message(sdev->ipc,
+ config->hdr.cmd, config, size, &reply,
+ sizeof(reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to set DAI config for ESAI%d\n",
+ config->dai_index);
+ return ret;
+ }
+
+ /* set config for all DAI's with name matching the link name */
+ ret = sof_set_dai_config(sdev, size, link, config);
+ if (ret < 0)
+ dev_err(sdev->dev, "error: failed to save DAI config for ESAI%d\n",
+ config->dai_index);
+
+ return ret;
}
static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
@@ -2828,6 +2988,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
if (!link->no_pcm) {
link->nonatomic = true;
+ /* set trigger order */
+ link->trigger[0] = SND_SOC_DPCM_TRIGGER_POST;
+ link->trigger[1] = SND_SOC_DPCM_TRIGGER_POST;
+
/* nothing more to do for FE dai links */
return 0;
}
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index 4c3cff031fd6..b0e4556c8536 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -162,6 +162,9 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev)
struct sof_ipc_reply ipc_reply;
int ret;
+ if (!sdev->dtrace_is_supported)
+ return 0;
+
if (sdev->dtrace_is_enabled || !sdev->dma_trace_pages)
return -EINVAL;
@@ -222,6 +225,9 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev)
{
int ret;
+ if (!sdev->dtrace_is_supported)
+ return 0;
+
/* set false before start initialization */
sdev->dtrace_is_enabled = false;
@@ -277,6 +283,9 @@ EXPORT_SYMBOL(snd_sof_init_trace);
int snd_sof_trace_update_pos(struct snd_sof_dev *sdev,
struct sof_ipc_dma_trace_posn *posn)
{
+ if (!sdev->dtrace_is_supported)
+ return 0;
+
if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) {
sdev->host_offset = posn->host_offset;
wake_up(&sdev->trace_sleep);
@@ -293,6 +302,9 @@ int snd_sof_trace_update_pos(struct snd_sof_dev *sdev,
/* an error has occurred within the DSP that prevents further trace */
void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev)
{
+ if (!sdev->dtrace_is_supported)
+ return;
+
if (sdev->dtrace_is_enabled) {
dev_err(sdev->dev, "error: waking up any trace sleepers\n");
sdev->dtrace_error = true;
@@ -305,7 +317,7 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev)
{
int ret;
- if (!sdev->dtrace_is_enabled)
+ if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled)
return;
ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP);
@@ -326,6 +338,9 @@ EXPORT_SYMBOL(snd_sof_release_trace);
void snd_sof_free_trace(struct snd_sof_dev *sdev)
{
+ if (!sdev->dtrace_is_supported)
+ return;
+
snd_sof_release_trace(sdev);
snd_dma_free_pages(&sdev->dmatb);
diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c
index d38ebbbbf169..da4b8f5f192b 100644
--- a/sound/soc/sprd/sprd-pcm-dma.c
+++ b/sound/soc/sprd/sprd-pcm-dma.c
@@ -46,12 +46,10 @@ static const struct snd_pcm_hardware sprd_pcm_hardware = {
.buffer_bytes_max = 64 * 1024,
};
-static int sprd_pcm_open(struct snd_pcm_substream *substream)
+static int sprd_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct sprd_pcm_dma_private *dma_private;
int hw_chan = SPRD_PCM_CHANNEL_MAX;
@@ -111,13 +109,11 @@ error:
return ret;
}
-static int sprd_pcm_close(struct snd_pcm_substream *substream)
+static int sprd_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sprd_pcm_dma_private *dma_private = runtime->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
int size = runtime->hw.periods_max * SPRD_PCM_DMA_LINKLIST_SIZE;
int i;
@@ -157,14 +153,12 @@ static void sprd_pcm_release_dma_channel(struct snd_pcm_substream *substream)
}
}
-static int sprd_pcm_request_dma_channel(struct snd_pcm_substream *substream,
+static int sprd_pcm_request_dma_channel(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
int channels)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sprd_pcm_dma_private *dma_private = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct sprd_pcm_dma_params *dma_params = dma_private->params;
int i;
@@ -190,14 +184,13 @@ static int sprd_pcm_request_dma_channel(struct snd_pcm_substream *substream,
return 0;
}
-static int sprd_pcm_hw_params(struct snd_pcm_substream *substream,
+static int sprd_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sprd_pcm_dma_private *dma_private = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct sprd_pcm_dma_params *dma_params;
size_t totsize = params_buffer_bytes(params);
size_t period = params_period_bytes(params);
@@ -218,7 +211,8 @@ static int sprd_pcm_hw_params(struct snd_pcm_substream *substream,
if (!dma_private->params) {
dma_private->params = dma_params;
- ret = sprd_pcm_request_dma_channel(substream, channels);
+ ret = sprd_pcm_request_dma_channel(component,
+ substream, channels);
if (ret)
return ret;
}
@@ -313,7 +307,8 @@ sg_err:
return ret;
}
-static int sprd_pcm_hw_free(struct snd_pcm_substream *substream)
+static int sprd_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
sprd_pcm_release_dma_channel(substream);
@@ -321,13 +316,11 @@ static int sprd_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int sprd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int sprd_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct sprd_pcm_dma_private *dma_private =
substream->runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
int ret = 0, i;
switch (cmd) {
@@ -387,13 +380,11 @@ static int sprd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
-static snd_pcm_uframes_t sprd_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t sprd_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sprd_pcm_dma_private *dma_private = runtime->private_data;
- struct snd_soc_component *component =
- snd_soc_rtdcom_lookup(rtd, DRV_NAME);
int pointer[SPRD_PCM_CHANNEL_MAX];
int bytes_of_pointer = 0, sel_max = 0, i;
snd_pcm_uframes_t x;
@@ -444,7 +435,8 @@ static snd_pcm_uframes_t sprd_pcm_pointer(struct snd_pcm_substream *substream)
return x;
}
-static int sprd_pcm_mmap(struct snd_pcm_substream *substream,
+static int sprd_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -456,18 +448,8 @@ static int sprd_pcm_mmap(struct snd_pcm_substream *substream,
vma->vm_page_prot);
}
-static struct snd_pcm_ops sprd_pcm_ops = {
- .open = sprd_pcm_open,
- .close = sprd_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = sprd_pcm_hw_params,
- .hw_free = sprd_pcm_hw_free,
- .trigger = sprd_pcm_trigger,
- .pointer = sprd_pcm_pointer,
- .mmap = sprd_pcm_mmap,
-};
-
-static int sprd_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int sprd_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -506,7 +488,8 @@ static int sprd_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static void sprd_pcm_free(struct snd_pcm *pcm)
+static void sprd_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
int i;
@@ -523,10 +506,17 @@ static void sprd_pcm_free(struct snd_pcm *pcm)
static const struct snd_soc_component_driver sprd_soc_component = {
.name = DRV_NAME,
- .ops = &sprd_pcm_ops,
+ .open = sprd_pcm_open,
+ .close = sprd_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = sprd_pcm_hw_params,
+ .hw_free = sprd_pcm_hw_free,
+ .trigger = sprd_pcm_trigger,
+ .pointer = sprd_pcm_pointer,
+ .mmap = sprd_pcm_mmap,
+ .pcm_construct = sprd_pcm_new,
+ .pcm_destruct = sprd_pcm_free,
.compr_ops = &sprd_platform_compr_ops,
- .pcm_new = sprd_pcm_new,
- .pcm_free = sprd_pcm_free,
};
static int sprd_soc_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
index 3c9a9deec9af..81c407da15c5 100644
--- a/sound/soc/stm/stm32_adfsdm.c
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -210,7 +210,8 @@ static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private)
return 0;
}
-static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int stm32_adfsdm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct stm32_adfsdm_priv *priv =
@@ -230,7 +231,8 @@ static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd)
return -EINVAL;
}
-static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream)
+static int stm32_adfsdm_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
@@ -243,7 +245,8 @@ static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream)
return ret;
}
-static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream)
+static int stm32_adfsdm_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct stm32_adfsdm_priv *priv =
@@ -256,6 +259,7 @@ static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream)
}
static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
+ struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -265,7 +269,8 @@ static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
return bytes_to_frames(substream->runtime, priv->pos);
}
-static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream,
+static int stm32_adfsdm_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -282,23 +287,16 @@ static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream,
params_period_size(params));
}
-static int stm32_adfsdm_pcm_hw_free(struct snd_pcm_substream *substream)
+static int stm32_adfsdm_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_pcm_lib_free_pages(substream);
return 0;
}
-static struct snd_pcm_ops stm32_adfsdm_pcm_ops = {
- .open = stm32_adfsdm_pcm_open,
- .close = stm32_adfsdm_pcm_close,
- .hw_params = stm32_adfsdm_pcm_hw_params,
- .hw_free = stm32_adfsdm_pcm_hw_free,
- .trigger = stm32_adfsdm_trigger,
- .pointer = stm32_adfsdm_pcm_pointer,
-};
-
-static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int stm32_adfsdm_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
struct stm32_adfsdm_priv *priv =
@@ -310,7 +308,8 @@ static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm)
+static void stm32_adfsdm_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
@@ -320,9 +319,14 @@ static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm)
}
static struct snd_soc_component_driver stm32_adfsdm_soc_platform = {
- .ops = &stm32_adfsdm_pcm_ops,
- .pcm_new = stm32_adfsdm_pcm_new,
- .pcm_free = stm32_adfsdm_pcm_free,
+ .open = stm32_adfsdm_pcm_open,
+ .close = stm32_adfsdm_pcm_close,
+ .hw_params = stm32_adfsdm_pcm_hw_params,
+ .hw_free = stm32_adfsdm_pcm_hw_free,
+ .trigger = stm32_adfsdm_trigger,
+ .pointer = stm32_adfsdm_pcm_pointer,
+ .pcm_construct = stm32_adfsdm_pcm_new,
+ .pcm_destruct = stm32_adfsdm_pcm_free,
};
static const struct of_device_id stm32_adfsdm_of_match[] = {
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
index ef4273361d0d..e20267504b16 100644
--- a/sound/soc/stm/stm32_sai.c
+++ b/sound/soc/stm/stm32_sai.c
@@ -100,7 +100,7 @@ static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n",
sai->pdev->dev.of_node,
prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
- stm32_sai_pclk_disable(&sai->pdev->dev);
+ stm32_sai_pclk_disable(&sai->pdev->dev);
return -EINVAL;
}
diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c
index cd4b235fce57..3fd28ee01675 100644
--- a/sound/soc/stm/stm32_spdifrx.c
+++ b/sound/soc/stm/stm32_spdifrx.c
@@ -351,6 +351,8 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx)
SPDIFRX_CR_CUMSK | SPDIFRX_CR_PTMSK | SPDIFRX_CR_RXSTEO;
cr_mask = cr;
+ cr |= SPDIFRX_CR_NBTRSET(SPDIFRX_NBTR_63);
+ cr_mask |= SPDIFRX_CR_NBTR_MASK;
cr |= SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC);
cr_mask |= SPDIFRX_CR_SPDIFEN_MASK;
ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
@@ -666,7 +668,7 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
struct snd_pcm_substream *substream = spdifrx->substream;
struct platform_device *pdev = spdifrx->pdev;
unsigned int cr, mask, sr, imr;
- unsigned int flags;
+ unsigned int flags, sync_state;
int err = 0, err_xrun = 0;
regmap_read(spdifrx->regmap, STM32_SPDIFRX_SR, &sr);
@@ -726,11 +728,23 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
}
if (err) {
- /* SPDIFRX in STATE_STOP. Disable SPDIFRX to clear errors */
+ regmap_read(spdifrx->regmap, STM32_SPDIFRX_CR, &cr);
+ sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) &&
+ SPDIFRX_SPDIFEN_SYNC;
+
+ /* SPDIFRX is in STATE_STOP. Disable SPDIFRX to clear errors */
cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_DISABLE);
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
SPDIFRX_CR_SPDIFEN_MASK, cr);
+ /* If SPDIFRX was in STATE_SYNC, retry synchro */
+ if (sync_state) {
+ cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC);
+ regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
+ SPDIFRX_CR_SPDIFEN_MASK, cr);
+ return IRQ_HANDLED;
+ }
+
if (substream)
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index ee448d5e07a6..34f3e0be3058 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -1442,7 +1442,7 @@ static struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev)
if (!aux_dev.dlc.of_node) {
dev_err(dev, "Can't find analog controls for codec.\n");
return ERR_PTR(-EINVAL);
- };
+ }
card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
if (!card->dai_link)
@@ -1480,7 +1480,7 @@ static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev)
if (!aux_dev.dlc.of_node) {
dev_err(dev, "Can't find analog controls for codec.\n");
return ERR_PTR(-EINVAL);
- };
+ }
card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
if (!card->dai_link)
@@ -1518,7 +1518,7 @@ static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev)
if (!aux_dev.dlc.of_node) {
dev_err(dev, "Can't find analog controls for codec.\n");
return ERR_PTR(-EINVAL);
- };
+ }
card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
if (!card->dai_link)
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index e6d548fa980b..dbed3c5408e7 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -127,7 +127,7 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
struct device *dev = dai->dev;
struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
unsigned int mask, val, reg;
- int ret, sample_size, srate, i2sclock, bitcnt;
+ int ret, sample_size, srate, i2sclock, bitcnt, audio_bits;
struct tegra30_ahub_cif_conf cif_conf;
if (params_channels(params) != 2)
@@ -137,8 +137,19 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
val = TEGRA30_I2S_CTRL_BIT_SIZE_16;
+ audio_bits = TEGRA30_AUDIOCIF_BITS_16;
sample_size = 16;
break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = TEGRA30_I2S_CTRL_BIT_SIZE_24;
+ audio_bits = TEGRA30_AUDIOCIF_BITS_24;
+ sample_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val = TEGRA30_I2S_CTRL_BIT_SIZE_32;
+ audio_bits = TEGRA30_AUDIOCIF_BITS_32;
+ sample_size = 32;
+ break;
default:
return -EINVAL;
}
@@ -170,8 +181,8 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
cif_conf.threshold = 0;
cif_conf.audio_channels = 2;
cif_conf.client_channels = 2;
- cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16;
- cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = audio_bits;
cif_conf.expand = 0;
cif_conf.stereo_conv = 0;
cif_conf.replicate = 0;
@@ -220,9 +231,9 @@ static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s)
static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s)
{
- tegra30_ahub_disable_rx_fifo(i2s->capture_fifo_cif);
regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL,
TEGRA30_I2S_CTRL_XFER_EN_RX, 0);
+ tegra30_ahub_disable_rx_fifo(i2s->capture_fifo_cif);
}
static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -254,6 +265,34 @@ static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
+static int tegra30_i2s_set_tdm(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask, val;
+
+ dev_dbg(dai->dev, "%s: txmask=0x%08x rxmask=0x%08x slots=%d width=%d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ mask = TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK |
+ TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK |
+ TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK;
+
+ val = (tx_mask << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT) |
+ (rx_mask << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT) |
+ ((slots - 1) << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT);
+
+ pm_runtime_get_sync(dai->dev);
+ regmap_update_bits(i2s->regmap, TEGRA30_I2S_SLOT_CTRL, mask, val);
+ /* set the fsync width to minimum of 1 clock width */
+ regmap_update_bits(i2s->regmap, TEGRA30_I2S_CH_CTRL,
+ TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK, 0x0);
+ pm_runtime_put(dai->dev);
+
+ return 0;
+}
+
static int tegra30_i2s_probe(struct snd_soc_dai *dai)
{
struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
@@ -268,6 +307,7 @@ static const struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
.set_fmt = tegra30_i2s_set_fmt,
.hw_params = tegra30_i2s_hw_params,
.trigger = tegra30_i2s_trigger,
+ .set_tdm_slot = tegra30_i2s_set_tdm,
};
static const struct snd_soc_dai_driver tegra30_i2s_dai_template = {
@@ -277,14 +317,18 @@ static const struct snd_soc_dai_driver tegra30_i2s_dai_template = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &tegra30_i2s_dai_ops,
.symmetric_rates = 1,
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index 7aa3c32e4a49..8e5371801d88 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -1867,7 +1867,7 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
return PCM_EDMA;
tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data;
- chan = dma_request_slave_channel_reason(mcasp->dev, tmp);
+ chan = dma_request_chan(mcasp->dev, tmp);
if (IS_ERR(chan)) {
if (PTR_ERR(chan) != -EPROBE_DEFER)
dev_err(mcasp->dev,
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index 66044559f70f..33c78d33e5a1 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -47,12 +47,12 @@ static const struct snd_pcm_hardware txx9aclc_pcm_hardware = {
.buffer_bytes_max = 32 * 1024,
};
-static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream,
+static int txx9aclc_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct txx9aclc_dmadata *dmadata = runtime->private_data;
int ret;
@@ -76,12 +76,14 @@ static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int txx9aclc_pcm_hw_free(struct snd_pcm_substream *substream)
+static int txx9aclc_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
-static int txx9aclc_pcm_prepare(struct snd_pcm_substream *substream)
+static int txx9aclc_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct txx9aclc_dmadata *dmadata = runtime->private_data;
@@ -203,7 +205,8 @@ static void txx9aclc_dma_tasklet(unsigned long data)
spin_unlock_irqrestore(&dmadata->dma_lock, flags);
}
-static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int txx9aclc_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata;
@@ -236,14 +239,16 @@ static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
static snd_pcm_uframes_t
-txx9aclc_pcm_pointer(struct snd_pcm_substream *substream)
+txx9aclc_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
return bytes_to_frames(substream->runtime, dmadata->pos);
}
-static int txx9aclc_pcm_open(struct snd_pcm_substream *substream)
+static int txx9aclc_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct txx9aclc_soc_device *dev = &txx9aclc_soc_device;
struct txx9aclc_dmadata *dmadata = &dev->dmadata[substream->stream];
@@ -261,7 +266,8 @@ static int txx9aclc_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int txx9aclc_pcm_close(struct snd_pcm_substream *substream)
+static int txx9aclc_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
struct dma_chan *chan = dmadata->dma_chan;
@@ -271,23 +277,12 @@ static int txx9aclc_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static const struct snd_pcm_ops txx9aclc_pcm_ops = {
- .open = txx9aclc_pcm_open,
- .close = txx9aclc_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = txx9aclc_pcm_hw_params,
- .hw_free = txx9aclc_pcm_hw_free,
- .prepare = txx9aclc_pcm_prepare,
- .trigger = txx9aclc_pcm_trigger,
- .pointer = txx9aclc_pcm_pointer,
-};
-
-static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int txx9aclc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct platform_device *pdev = to_platform_device(component->dev);
struct txx9aclc_soc_device *dev;
struct resource *r;
@@ -409,8 +404,15 @@ static const struct snd_soc_component_driver txx9aclc_soc_component = {
.name = DRV_NAME,
.probe = txx9aclc_pcm_probe,
.remove = txx9aclc_pcm_remove,
- .ops = &txx9aclc_pcm_ops,
- .pcm_new = txx9aclc_pcm_new,
+ .open = txx9aclc_pcm_open,
+ .close = txx9aclc_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = txx9aclc_pcm_hw_params,
+ .hw_free = txx9aclc_pcm_hw_free,
+ .prepare = txx9aclc_pcm_prepare,
+ .trigger = txx9aclc_pcm_trigger,
+ .pointer = txx9aclc_pcm_pointer,
+ .pcm_construct = txx9aclc_pcm_new,
};
static int txx9aclc_soc_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c
index e8446cc4e8f8..700d936ed94e 100644
--- a/sound/soc/uniphier/aio-dma.c
+++ b/sound/soc/uniphier/aio-dma.c
@@ -93,7 +93,8 @@ static irqreturn_t aiodma_irq(int irq, void *p)
return ret;
}
-static int uniphier_aiodma_open(struct snd_pcm_substream *substream)
+static int uniphier_aiodma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -103,7 +104,8 @@ static int uniphier_aiodma_open(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256);
}
-static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream,
+static int uniphier_aiodma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
@@ -112,7 +114,8 @@ static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream)
+static int uniphier_aiodma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
substream->runtime->dma_bytes = 0;
@@ -120,7 +123,8 @@ static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream)
+static int uniphier_aiodma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
@@ -146,7 +150,8 @@ static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd)
+static int uniphier_aiodma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
@@ -181,6 +186,7 @@ static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd)
}
static snd_pcm_uframes_t uniphier_aiodma_pointer(
+ struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -204,7 +210,8 @@ static snd_pcm_uframes_t uniphier_aiodma_pointer(
return pos;
}
-static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream,
+static int uniphier_aiodma_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
@@ -214,18 +221,8 @@ static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
-static const struct snd_pcm_ops uniphier_aiodma_ops = {
- .open = uniphier_aiodma_open,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = uniphier_aiodma_hw_params,
- .hw_free = uniphier_aiodma_hw_free,
- .prepare = uniphier_aiodma_prepare,
- .trigger = uniphier_aiodma_trigger,
- .pointer = uniphier_aiodma_pointer,
- .mmap = uniphier_aiodma_mmap,
-};
-
-static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd)
+static int uniphier_aiodma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct device *dev = rtd->card->snd_card->dev;
struct snd_pcm *pcm = rtd->pcm;
@@ -242,16 +239,24 @@ static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static void uniphier_aiodma_free(struct snd_pcm *pcm)
+static void uniphier_aiodma_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static const struct snd_soc_component_driver uniphier_soc_platform = {
- .pcm_new = uniphier_aiodma_new,
- .pcm_free = uniphier_aiodma_free,
- .ops = &uniphier_aiodma_ops,
- .compr_ops = &uniphier_aio_compr_ops,
+ .open = uniphier_aiodma_open,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = uniphier_aiodma_hw_params,
+ .hw_free = uniphier_aiodma_hw_free,
+ .prepare = uniphier_aiodma_prepare,
+ .trigger = uniphier_aiodma_trigger,
+ .pointer = uniphier_aiodma_pointer,
+ .mmap = uniphier_aiodma_mmap,
+ .pcm_construct = uniphier_aiodma_new,
+ .pcm_destruct = uniphier_aiodma_free,
+ .compr_ops = &uniphier_aio_compr_ops,
};
static const struct regmap_config aiodma_regmap_config = {
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index a90e0d7f0b73..394d8b2a4a16 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -533,7 +533,6 @@ static void disable_msp_tx(struct ux500_msp *msp)
static int disable_msp(struct ux500_msp *msp, unsigned int dir)
{
u32 reg_val_GCR;
- int status = 0;
unsigned int disable_tx, disable_rx;
reg_val_GCR = readl(msp->registers + MSP_GCR);
@@ -566,7 +565,7 @@ static int disable_msp(struct ux500_msp *msp, unsigned int dir)
else if (disable_rx)
disable_msp_rx(msp);
- return status;
+ return 0;
}
int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c
index 48970efe7838..296c4caf96a0 100644
--- a/sound/soc/xilinx/xlnx_formatter_pcm.c
+++ b/sound/soc/xilinx/xlnx_formatter_pcm.c
@@ -313,16 +313,14 @@ static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg)
return IRQ_NONE;
}
-static int xlnx_formatter_pcm_open(struct snd_pcm_substream *substream)
+static int xlnx_formatter_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
int err;
u32 val, data_format_mode;
u32 ch_count_mask, ch_count_shift, data_xfer_mode, data_xfer_shift;
struct xlnx_pcm_stream_param *stream_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
- DRV_NAME);
struct xlnx_pcm_drv_data *adata = dev_get_drvdata(component->dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
@@ -387,14 +385,12 @@ static int xlnx_formatter_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int xlnx_formatter_pcm_close(struct snd_pcm_substream *substream)
+static int xlnx_formatter_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
int ret;
struct xlnx_pcm_stream_param *stream_data =
substream->runtime->private_data;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
- DRV_NAME);
ret = xlnx_formatter_pcm_reset(stream_data->mmio);
if (ret) {
@@ -409,7 +405,8 @@ err_reset:
}
static snd_pcm_uframes_t
-xlnx_formatter_pcm_pointer(struct snd_pcm_substream *substream)
+xlnx_formatter_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
u32 pos;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -423,16 +420,14 @@ xlnx_formatter_pcm_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, pos);
}
-static int xlnx_formatter_pcm_hw_params(struct snd_pcm_substream *substream,
+static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
u32 low, high, active_ch, val, bytes_per_ch, bits_per_sample;
u32 aes_reg1_val, aes_reg2_val;
int status;
u64 size;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
- DRV_NAME);
struct snd_pcm_runtime *runtime = substream->runtime;
struct xlnx_pcm_stream_param *stream_data = runtime->private_data;
@@ -500,12 +495,14 @@ static int xlnx_formatter_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int xlnx_formatter_pcm_hw_free(struct snd_pcm_substream *substream)
+static int xlnx_formatter_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
-static int xlnx_formatter_pcm_trigger(struct snd_pcm_substream *substream,
+static int xlnx_formatter_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
int cmd)
{
u32 val;
@@ -532,10 +529,9 @@ static int xlnx_formatter_pcm_trigger(struct snd_pcm_substream *substream,
return 0;
}
-static int xlnx_formatter_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int xlnx_formatter_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd,
- DRV_NAME);
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
SNDRV_DMA_TYPE_DEV, component->dev,
xlnx_pcm_hardware.buffer_bytes_max,
@@ -543,20 +539,16 @@ static int xlnx_formatter_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static const struct snd_pcm_ops xlnx_formatter_pcm_ops = {
- .open = xlnx_formatter_pcm_open,
- .close = xlnx_formatter_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = xlnx_formatter_pcm_hw_params,
- .hw_free = xlnx_formatter_pcm_hw_free,
- .trigger = xlnx_formatter_pcm_trigger,
- .pointer = xlnx_formatter_pcm_pointer,
-};
-
static const struct snd_soc_component_driver xlnx_asoc_component = {
- .name = DRV_NAME,
- .ops = &xlnx_formatter_pcm_ops,
- .pcm_new = xlnx_formatter_pcm_new,
+ .name = DRV_NAME,
+ .open = xlnx_formatter_pcm_open,
+ .close = xlnx_formatter_pcm_close,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+ .hw_params = xlnx_formatter_pcm_hw_params,
+ .hw_free = xlnx_formatter_pcm_hw_free,
+ .trigger = xlnx_formatter_pcm_trigger,
+ .pointer = xlnx_formatter_pcm_pointer,
+ .pcm_construct = xlnx_formatter_pcm_new,
};
static int xlnx_formatter_pcm_probe(struct platform_device *pdev)
@@ -564,7 +556,6 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev)
int ret;
u32 val;
struct xlnx_pcm_drv_data *aud_drv_data;
- struct resource *res;
struct device *dev = &pdev->dev;
aud_drv_data = devm_kzalloc(dev, sizeof(*aud_drv_data), GFP_KERNEL);
@@ -584,13 +575,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev)
return ret;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "audio formatter node:addr to resource failed\n");
- ret = -ENXIO;
- goto clk_err;
- }
- aud_drv_data->mmio = devm_ioremap_resource(dev, res);
+ aud_drv_data->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(aud_drv_data->mmio)) {
dev_err(dev, "audio formatter ioremap failed\n");
ret = PTR_ERR(aud_drv_data->mmio);
diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c
index efd374f114a0..e08f4fee932a 100644
--- a/sound/soc/xtensa/xtfpga-i2s.c
+++ b/sound/soc/xtensa/xtfpga-i2s.c
@@ -365,7 +365,8 @@ static const struct snd_pcm_hardware xtfpga_pcm_hardware = {
.fifo_size = 16,
};
-static int xtfpga_pcm_open(struct snd_pcm_substream *substream)
+static int xtfpga_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -378,13 +379,15 @@ static int xtfpga_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int xtfpga_pcm_close(struct snd_pcm_substream *substream)
+static int xtfpga_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
synchronize_rcu();
return 0;
}
-static int xtfpga_pcm_hw_params(struct snd_pcm_substream *substream,
+static int xtfpga_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
int ret;
@@ -424,7 +427,8 @@ static int xtfpga_pcm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static int xtfpga_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int xtfpga_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -452,7 +456,8 @@ static int xtfpga_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
-static snd_pcm_uframes_t xtfpga_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t xtfpga_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct xtfpga_i2s *i2s = runtime->private_data;
@@ -461,7 +466,8 @@ static snd_pcm_uframes_t xtfpga_pcm_pointer(struct snd_pcm_substream *substream)
return pos < runtime->buffer_size ? pos : 0;
}
-static int xtfpga_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int xtfpga_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
size_t size = xtfpga_pcm_hardware.buffer_bytes_max;
@@ -471,19 +477,15 @@ static int xtfpga_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static const struct snd_pcm_ops xtfpga_pcm_ops = {
+static const struct snd_soc_component_driver xtfpga_i2s_component = {
+ .name = DRV_NAME,
.open = xtfpga_pcm_open,
.close = xtfpga_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
+ .ioctl = snd_soc_pcm_lib_ioctl,
.hw_params = xtfpga_pcm_hw_params,
.trigger = xtfpga_pcm_trigger,
.pointer = xtfpga_pcm_pointer,
-};
-
-static const struct snd_soc_component_driver xtfpga_i2s_component = {
- .name = DRV_NAME,
- .pcm_new = xtfpga_pcm_new,
- .ops = &xtfpga_pcm_ops,
+ .pcm_construct = xtfpga_pcm_new,
};
static const struct snd_soc_dai_ops xtfpga_i2s_dai_ops = {