Merge branch 'asoc-5.5' into asoc-next
authorMark Brown <broonie@kernel.org>
Fri, 22 Nov 2019 19:56:02 +0000 (19:56 +0000)
committerMark Brown <broonie@kernel.org>
Fri, 22 Nov 2019 19:56:02 +0000 (19:56 +0000)
228 files changed:
Documentation/devicetree/bindings/sound/adi,adau7118.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/arndale.txt
Documentation/devicetree/bindings/sound/fsl,mqs.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt
Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt
Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt
Documentation/devicetree/bindings/sound/renesas,fsi.txt [deleted file]
Documentation/devicetree/bindings/sound/renesas,fsi.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/rockchip-max98090.txt
Documentation/devicetree/bindings/sound/rt1011.txt
Documentation/devicetree/bindings/sound/rt5682.txt
Documentation/devicetree/bindings/sound/samsung,odroid.txt [deleted file]
Documentation/devicetree/bindings/sound/samsung,odroid.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/samsung-i2s.txt [deleted file]
Documentation/devicetree/bindings/sound/samsung-i2s.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/sun4i-codec.txt [deleted file]
Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt [deleted file]
Documentation/devicetree/bindings/sound/tas2562.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/tas2770.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ti,pcm3168a.txt
Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
Documentation/devicetree/bindings/vendor-prefixes.yaml
MAINTAINERS
drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
drivers/platform/chrome/cros_ec_trace.c
include/drm/bridge/dw_hdmi.h
include/dt-bindings/sound/samsung-i2s.h
include/linux/platform_data/cros_ec_commands.h
include/sound/dmaengine_pcm.h
include/sound/hda_codec.h
include/sound/pxa2xx-lib.h
include/sound/rt5682.h
include/sound/simple_card_utils.h
include/sound/soc-acpi-intel-match.h
include/sound/soc-acpi.h
include/sound/soc-component.h
include/sound/soc-dpcm.h
include/sound/soc.h
include/sound/sof.h
include/sound/sof/dai-imx.h [new file with mode: 0644]
include/sound/sof/dai.h
include/sound/sof/header.h
include/sound/sof/pm.h
include/sound/sof/stream.h
include/sound/wm8904.h
include/uapi/sound/compress_params.h
include/uapi/sound/sof/abi.h
include/uapi/sound/sof/tokens.h
sound/arm/pxa2xx-pcm-lib.c
sound/core/pcm_dmaengine.c
sound/pci/hda/patch_hdmi.c
sound/soc/amd/acp-pcm-dma.c
sound/soc/amd/raven/acp3x-pcm-dma.c
sound/soc/atmel/atmel-pcm-pdc.c
sound/soc/au1x/dbdma2.c
sound/soc/au1x/dma.c
sound/soc/bcm/cygnus-pcm.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/adau1761.c
sound/soc/codecs/adau7118-hw.c [new file with mode: 0644]
sound/soc/codecs/adau7118-i2c.c [new file with mode: 0644]
sound/soc/codecs/adau7118.c [new file with mode: 0644]
sound/soc/codecs/adau7118.h [new file with mode: 0644]
sound/soc/codecs/cros_ec_codec.c
sound/soc/codecs/cx2072x.c
sound/soc/codecs/hdac_hda.c
sound/soc/codecs/hdac_hda.h
sound/soc/codecs/madera.h
sound/soc/codecs/msm8916-wcd-analog.c
sound/soc/codecs/mt6358.c
sound/soc/codecs/pcm3168a.c
sound/soc/codecs/rt1011.c
sound/soc/codecs/rt1011.h
sound/soc/codecs/rt5514-spi.c
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5663.c
sound/soc/codecs/rt5677-spi.c
sound/soc/codecs/rt5677-spi.h
sound/soc/codecs/rt5677.c
sound/soc/codecs/rt5677.h
sound/soc/codecs/rt5682.c
sound/soc/codecs/tas2562.c [new file with mode: 0644]
sound/soc/codecs/tas2562.h [new file with mode: 0644]
sound/soc/codecs/tas2770.c [new file with mode: 0644]
sound/soc/codecs/tas2770.h [new file with mode: 0644]
sound/soc/codecs/tlv320aic31xx.c
sound/soc/codecs/tlv320aic31xx.h
sound/soc/codecs/tlv320aic32x4.c
sound/soc/codecs/wcd9335.c
sound/soc/codecs/wm2200.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8904.h
sound/soc/codecs/wm8958-dsp2.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8994.h
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/dwc/dwc-pcm.c
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/fsl_asrc.c
sound/soc/fsl/fsl_asrc.h
sound/soc/fsl/fsl_asrc_dma.c
sound/soc/fsl/fsl_audmix.c
sound/soc/fsl/fsl_audmix.h
sound/soc/fsl/fsl_dma.c
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_mqs.c [new file with mode: 0644]
sound/soc/fsl/imx-pcm-fiq.c
sound/soc/fsl/mpc5200_dma.c
sound/soc/generic/audio-graph-card.c
sound/soc/generic/simple-card.c
sound/soc/intel/Kconfig
sound/soc/intel/atom/sst-mfld-platform-pcm.c
sound/soc/intel/baytrail/sst-baytrail-pcm.c
sound/soc/intel/boards/Kconfig
sound/soc/intel/boards/Makefile
sound/soc/intel/boards/bdw-rt5677.c
sound/soc/intel/boards/bxt_da7219_max98357a.c
sound/soc/intel/boards/bxt_rt298.c
sound/soc/intel/boards/cht_bsw_rt5645.c
sound/soc/intel/boards/cml_rt1011_rt5682.c [new file with mode: 0644]
sound/soc/intel/boards/glk_rt5682_max98357a.c
sound/soc/intel/boards/hda_dsp_common.c [new file with mode: 0644]
sound/soc/intel/boards/hda_dsp_common.h [new file with mode: 0644]
sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
sound/soc/intel/boards/skl_hda_dsp_common.c
sound/soc/intel/boards/skl_hda_dsp_common.h
sound/soc/intel/boards/skl_hda_dsp_generic.c
sound/soc/intel/boards/sof_rt5682.c
sound/soc/intel/common/Makefile
sound/soc/intel/common/soc-acpi-intel-cfl-match.c [new file with mode: 0644]
sound/soc/intel/common/soc-acpi-intel-cml-match.c [new file with mode: 0644]
sound/soc/intel/common/soc-acpi-intel-cnl-match.c
sound/soc/intel/common/soc-acpi-intel-jsl-match.c [new file with mode: 0644]
sound/soc/intel/haswell/sst-haswell-pcm.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/jz4740/jz4740-i2s.c
sound/soc/kirkwood/kirkwood-dma.c
sound/soc/mediatek/Kconfig
sound/soc/mediatek/common/mtk-afe-platform-driver.c
sound/soc/mediatek/common/mtk-afe-platform-driver.h
sound/soc/mediatek/common/mtk-btcvsd.c
sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
sound/soc/meson/axg-fifo.c
sound/soc/meson/axg-fifo.h
sound/soc/meson/axg-frddr.c
sound/soc/meson/axg-toddr.c
sound/soc/pxa/mmp-pcm.c
sound/soc/pxa/poodle.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/pxa/pxa2xx-i2s.c
sound/soc/pxa/pxa2xx-pcm.c
sound/soc/qcom/lpass-platform.c
sound/soc/qcom/qdsp6/q6asm-dai.c
sound/soc/qcom/qdsp6/q6asm.c
sound/soc/qcom/qdsp6/q6asm.h
sound/soc/qcom/qdsp6/q6routing.c
sound/soc/rockchip/Kconfig
sound/soc/rockchip/rockchip_max98090.c
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/arndale.c [new file with mode: 0644]
sound/soc/samsung/arndale_rt5631.c [deleted file]
sound/soc/samsung/idma.c
sound/soc/sh/dma-sh7760.c
sound/soc/sh/fsi.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/dma.c
sound/soc/sh/siu_pcm.c
sound/soc/soc-component.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-generic-dmaengine-pcm.c
sound/soc/soc-ops.c
sound/soc/soc-pcm.c
sound/soc/soc-topology.c
sound/soc/soc-utils.c
sound/soc/sof/Kconfig
sound/soc/sof/control.c
sound/soc/sof/core.c
sound/soc/sof/debug.c
sound/soc/sof/imx/Kconfig
sound/soc/sof/imx/imx8.c
sound/soc/sof/intel/Kconfig
sound/soc/sof/intel/apl.c
sound/soc/sof/intel/bdw.c
sound/soc/sof/intel/byt.c
sound/soc/sof/intel/cnl.c
sound/soc/sof/intel/hda-codec.c
sound/soc/sof/intel/hda-dsp.c
sound/soc/sof/intel/hda-ipc.c
sound/soc/sof/intel/hda-ipc.h [new file with mode: 0644]
sound/soc/sof/intel/hda-loader.c
sound/soc/sof/intel/hda-pcm.c
sound/soc/sof/intel/hda-stream.c
sound/soc/sof/intel/hda.c
sound/soc/sof/intel/hda.h
sound/soc/sof/ipc.c
sound/soc/sof/ops.h
sound/soc/sof/pcm.c
sound/soc/sof/pm.c
sound/soc/sof/sof-acpi-dev.c
sound/soc/sof/sof-pci-dev.c
sound/soc/sof/sof-priv.h
sound/soc/sof/topology.c
sound/soc/sof/trace.c
sound/soc/sprd/sprd-pcm-dma.c
sound/soc/stm/stm32_adfsdm.c
sound/soc/stm/stm32_sai.c
sound/soc/stm/stm32_spdifrx.c
sound/soc/sunxi/sun4i-codec.c
sound/soc/tegra/tegra30_i2s.c
sound/soc/ti/davinci-mcasp.c
sound/soc/txx9/txx9aclc.c
sound/soc/uniphier/aio-dma.c
sound/soc/ux500/ux500_msp_i2s.c
sound/soc/xilinx/xlnx_formatter_pcm.c
sound/soc/xtensa/xtfpga-i2s.c

diff --git a/Documentation/devicetree/bindings/sound/adi,adau7118.yaml b/Documentation/devicetree/bindings/sound/adi,adau7118.yaml
new file mode 100644 (file)
index 0000000..75e0cbe
--- /dev/null
@@ -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 (file)
index 0000000..b8f89c7
--- /dev/null
@@ -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 (file)
index 0000000..85305b4
--- /dev/null
@@ -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>;
+    };
+
+...
index 0e76946..1753012 100644 (file)
@@ -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 (file)
index 0000000..40353fc
--- /dev/null
@@ -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";
+};
index 1084f7f..8ca52dc 100644 (file)
@@ -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>;
        };
 };
index 396ba38..1f1cba4 100644 (file)
@@ -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>,
index d6d5207..decaa01 100644 (file)
@@ -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 (file)
index 0cf0f81..0000000
+++ /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 (file)
index 0000000..140a37f
--- /dev/null
@@ -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;
+    };
index 5c52182..797fd03 100644 (file)
@@ -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)
index a805aa9..e9c58b2 100644 (file)
@@ -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>;
 };
index 35a23e6..02d53b9 100644 (file)
@@ -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>;
 };
index 312e9a1..30e927a 100644 (file)
@@ -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 (file)
index e9da220..0000000
+++ /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 (file)
index 0000000..c6b2443
--- /dev/null
@@ -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 (file)
index a88cb00..0000000
+++ /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 (file)
index 0000000..53e3bad
--- /dev/null
@@ -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 (file)
index 66579bb..0000000
+++ /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 (file)
index 0735675..0000000
+++ /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 (file)
index 0000000..658e1fb
--- /dev/null
@@ -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 (file)
index 0000000..ede6bb3
--- /dev/null
@@ -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>;
+        };
+
index 5d9cb84..a02ecaa 100644 (file)
@@ -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>;
index 5b3c33b..e372303 100644 (file)
@@ -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:
 
index 967e78c..67b89db 100644 (file)
@@ -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
index e4f170d..236bb36 100644 (file)
@@ -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
index 1d15cf9..6c2c44d 100644 (file)
@@ -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)
index 521d689..2102872 100644 (file)
@@ -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);
index 6f80ff4..5af1d66 100644 (file)
        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), \
index cf528c2..9a0c838 100644 (file)
@@ -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);
index 77545f1..250de0d 100644 (file)
@@ -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 */
index 9841568..6921088 100644 (file)
@@ -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 */
index c679f61..b652206 100644 (file)
@@ -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
index 9a0393c..ac18f42 100644 (file)
@@ -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;
index 6758fc1..0feaf16 100644 (file)
@@ -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 */
 
index bf2ee75..bc2c317 100644 (file)
@@ -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
index 31f76b6..bbdd154 100644 (file)
@@ -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) \
index 6c9929a..20c0bee 100644 (file)
@@ -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
index 35b38e4..c4c997b 100644 (file)
@@ -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;
index 5d80b2e..f8fadf0 100644 (file)
@@ -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 */
index e55aeb0..b654ebf 100644 (file)
@@ -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,
index f264c65..c28a1ed 100644 (file)
        .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
index 4640566..4791017 100644 (file)
@@ -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 (file)
index 0000000..e02fb0b
--- /dev/null
@@ -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
index 0f12350..c229565 100644 (file)
@@ -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;
 
index 10f00c0..bf3edd9 100644 (file)
@@ -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)
index 0038794..3cf2e0f 100644 (file)
@@ -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
index 0b71b38..7facefb 100644 (file)
@@ -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;
 
index 1407440..88ac187 100644 (file)
  * 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/
index 3d4d6de..9c96fb0 100644 (file)
@@ -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
index a0fe0d4..ebfdc20 100644 (file)
@@ -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 */
index 8f99685..76883e6 100644 (file)
 /* 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
index 54500bd..a86c95d 100644 (file)
@@ -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");
index 89a0592..5749a8a 100644 (file)
@@ -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");
index 78bd2e3..d24bd95 100644 (file)
@@ -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;
 
index 52225b4..4b9a27e 100644 (file)
@@ -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)
index bc4dfaf..60709e3 100644 (file)
@@ -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);
index ed095af..18a2fd0 100644 (file)
@@ -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)
index d56092a..4553108 100644 (file)
@@ -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)
index 1e98cc4..49556eb 100644 (file)
@@ -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)
index 8966b02..c654080 100644 (file)
@@ -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,
index 229cc89..ec01e4f 100644 (file)
@@ -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
index c498373..ddfd070 100644 (file)
@@ -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
index 977f5a6..5ca9b74 100644 (file)
 #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 (file)
index 0000000..45a5d2d
--- /dev/null
@@ -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 (file)
index 0000000..a821136
--- /dev/null
@@ -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 (file)
index 0000000..841229d
--- /dev/null
@@ -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 (file)
index 0000000..c65679a
--- /dev/null
@@ -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
index 3c1bd24..dd14caf 100644 (file)
@@ -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>
 #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");
index 1c1ba7b..2ad00ed 100644 (file)
@@ -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);
 
index 4570f66..6803d39 100644 (file)
 #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 | \
                        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.
index 6b1bd4f..e145cec 100644 (file)
@@ -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) \
index 1f3e8e2..6d8938a 100644 (file)
@@ -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
index e3d311f..f53235b 100644 (file)
 #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,
index bb737fd..1b830ea 100644 (file)
@@ -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 */
index 88b7569..9711fab 100644 (file)
@@ -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);
index be1e276..2552073 100644 (file)
@@ -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",
index 2d65983..68fadc1 100644 (file)
 #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)
 #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;
 };
 
index 892ea40..57ff5ae 100644 (file)
@@ -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,
 };
 
 /**
index 19662ee..92d6701 100644 (file)
@@ -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,
+       },
        { }
 };
 
index 2943692..e6c1ec6 100644 (file)
@@ -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__);
        }
index d681488..25e28be 100644 (file)
@@ -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"
 #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);
 
index 6ba3369..3af36ec 100644 (file)
@@ -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__ */
index 315a3d3..e9a051a 100644 (file)
 
 #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) {
index 213f4b8..944ae02 100644 (file)
 #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,
index 5370e4b..b1713ff 100644 (file)
@@ -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 (file)
index 0000000..729acd8
--- /dev/null
@@ -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 (file)
index 0000000..62e659a
--- /dev/null
@@ -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 (file)
index 0000000..54c8135
--- /dev/null
@@ -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 (file)
index 0000000..cbb8583
--- /dev/null
@@ -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__ */
index df627a0..f6f19fd 100644 (file)
@@ -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,
index cb02495..83a8c76 100644 (file)
@@ -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
index 68165de..b4e9a6c 100644 (file)
@@ -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);
index f318403..f11ffa2 100644 (file)
@@ -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);
index cf64e10..7b087d9 100644 (file)
@@ -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;
 }
index 4af0e51..91cc63c 100644 (file)
@@ -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);
index 9e8c564..7d7ea15 100644 (file)
@@ -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;
index c1bca52..de63404 100644 (file)
@@ -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
 
index 18535b3..ca42445 100644 (file)
@@ -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:
index d5fb7f5..15ce64a 100644 (file)
@@ -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);
 
index 1d6f2ab..41c4b12 100644 (file)
@@ -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>
 
 #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];
index 9b8bb7b..2a9b610 100644 (file)
@@ -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)
 {
index aa634ef..4c481cf 100644 (file)
@@ -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
index a9ae91c..de6fcc8 100644 (file)
@@ -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)
index aa99c00..65e8cd4 100644 (file)
@@ -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
index c0dd044..8cde88c 100644 (file)
@@ -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
index cfa40ef..a3cfcee 100644 (file)
@@ -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",
index c600751..2b57e8c 100644 (file)
@@ -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 */
index 01052a0..d6146de 100644 (file)
 
 #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);
index c7e4e97..a1db1bc 100644 (file)
@@ -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);
 
index 7812ffe..479f056 100644 (file)
@@ -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;
 };
 
index e225083..2868c4f 100644 (file)
@@ -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;
index a78e4ab..c7a49d0 100644 (file)
@@ -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 (file)
index 0000000..0c813a4
--- /dev/null
@@ -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");
index c49aea4..08131d1 100644 (file)
@@ -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,
index ccf9301..5237ac9 100644 (file)
@@ -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)
index 6007e63..9ad35d9 100644 (file)
@@ -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";
index fc9c753..10b82bf 100644 (file)
@@ -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";
index 01c9975..79b2276 100644 (file)
@@ -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
index 8cc3cc3..47e3d19 100644 (file)
@@ -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)
index 54f2ee3..1d780fc 100644 (file)
@@ -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
index 5c27f7a..6c9fd9a 100644 (file)
@@ -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
 
index 6445f90..ba1aa89 100644 (file)
@@ -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
index 4a4d335..2af8e5a 100644 (file)
@@ -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 */
index ac1dea5..5873abb 100644 (file)
@@ -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);
 }
 
index adf416a..eabf9d8 100644 (file)
@@ -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);
 }
 
index 8879c3b..c68a5b8 100644 (file)
@@ -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,15 +229,21 @@ 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 (file)
index 0000000..a22f972
--- /dev/null
@@ -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");
index bd2d371..b36264d 100644 (file)
@@ -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 (file)
index 0000000..ed36b68
--- /dev/null
@@ -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 (file)
index 0000000..431f7f0
--- /dev/null
@@ -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 */
index 74dda87..3e5f6be 100644 (file)
@@ -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);
 }
 
index 58409b6..eb419e1 100644 (file)
@@ -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),
index daa582e..d615067 100644 (file)
@@ -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 */
index 1778acd..4e45901 100644 (file)
@@ -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);
index 4f6e58c..751b8ea 100644 (file)
@@ -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,
index 18d9630..bd35287 100644 (file)
@@ -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 (file)
index 0000000..d6fd202
--- /dev/null
@@ -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 (file)
index 0000000..5d08ae0
--- /dev/null
@@ -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");
index 985aa36..2758884 100644 (file)
@@ -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 (file)
index 0000000..1c68a04
--- /dev/null
@@ -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");
index 7f4f6b7..6e498a5 100644 (file)
@@ -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,
index 7f28742..0850141 100644 (file)
@@ -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 */
 };
 
index 13408de..38d48d1 100644 (file)
@@ -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);
index 6f69f31..e28fb34 100644 (file)
@@ -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,
 };
index 111e44b..a656d20 100644 (file)
@@ -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.
index 3ce527c..b6624d8 100644 (file)
@@ -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);
 
index 88df679..e550d11 100644 (file)
@@ -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);
index d00608c..2b490ae 100644 (file)
@@ -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)
index e52c032..033c07f 100644 (file)
@@ -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)
index 4a31106..76af09d 100644 (file)
@@ -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);
index bb9cdc0..0555f7d 100644 (file)
@@ -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 =
index 5a37499..d6f3eef 100644 (file)
@@ -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)
 {
index bb1e2ce..cf928d4 100644 (file)
@@ -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);
index 6ab111c..665d75d 100644 (file)
@@ -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 = {
index c8ea214..7fef0b9 100644 (file)
@@ -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 = {
index 7096b52..54a4c92 100644 (file)
@@ -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)
index 48d5c22..59ef04d 100644 (file)
@@ -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
index 5fdd1a2..49cb3ba 100644 (file)
@@ -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
index bf28187..31e81a6 100644 (file)
@@ -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
index 9f7fb73..e77d707 100644 (file)
@@ -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)
index 74b56fa..07b3455 100644 (file)
 #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)
index 4c745ba..2e88923 100644 (file)
@@ -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)
index 548eb4f..8150c10 100644 (file)
@@ -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,
 };
 
index e8141a3..36e0eab 100644 (file)
@@ -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
  *
index 9f5fb57..6764f55 100644 (file)
@@ -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,
index ddcd997..2072410 100644 (file)
@@ -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,
index b43657e..d610b55 100644 (file)
@@ -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"
index e80b091..60930fa 100644 (file)
@@ -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,
index 6389831..1a0b163 100644 (file)
@@ -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"
index c3b7603..8f5dfe2 100644 (file)
@@ -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 (file)
index 0000000..d646029
--- /dev/null
@@ -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 (file)
index fd8c664..0000000
+++ /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");
index 65497cd..294dce1 100644 (file)
@@ -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)
index 5aee11c..93bb80d 100644 (file)
@@ -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)
index 3447dbd..e384fdc 100644 (file)
@@ -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,
 };
 
 /*
index a6c1cf9..399dc6e 100644 (file)
@@ -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,
index 28f65eb..95aa26d 100644 (file)
@@ -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),
index 78c3145..a5e21e5 100644 (file)
@@ -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);
index 79ffc28..1590e80 100644 (file)
@@ -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);
 }
index 9e54d8a..61f2303 100644 (file)
@@ -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;
index 88978a3..062653a 100644 (file)
@@ -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;
+       }
+
+       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 false;
+       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,26 +1204,24 @@ 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)
 {
-       /* For framework level robustness */
-       snd_soc_component_set_jack(component, NULL, NULL);
 
-       list_del_init(&component->card_list);
-       snd_soc_dapm_free(snd_soc_component_get_dapm(component));
-       soc_cleanup_component_debugfs(component);
-       component->card = NULL;
-       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);
+       if (probed)
+               snd_soc_component_remove(component);
+
+       /* For framework level robustness */
+       snd_soc_component_set_jack(component, NULL, NULL);
 
-       soc_cleanup_component(component);
+       list_del_init(&component->card_list);
+       snd_soc_dapm_free(snd_soc_component_get_dapm(component));
+       soc_cleanup_component_debugfs(component);
+       component->card = NULL;
+       snd_soc_component_module_put_when_remove(component);
 }
 
 static int soc_probe_component(struct snd_soc_card *card,
@@ -1001,6 +1230,7 @@ static int soc_probe_component(struct snd_soc_card *card,
        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;
@@ -2577,6 +2553,19 @@ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component,
        return dai;
 }
 
+/**
+ * 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
  *
@@ -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,
index 5552c66..a428ff3 100644 (file)
@@ -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;
index f4dc3d4..652657d 100644 (file)
@@ -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;
index b600d3e..01eb870 100644 (file)
@@ -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",
index 0fd0329..81d2af0 100644 (file)
@@ -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,
index 54dcece..2fd4562 100644 (file)
@@ -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 = {
index bb8036a..71a0fc0 100644 (file)
@@ -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
index 2b8711e..7baf7f1 100644 (file)
 /* 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,
index 81f28f7..805918d 100644 (file)
 #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);
index 5529e8e..d2b3b99 100644 (file)
@@ -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);
index 5acae75..71f318b 100644 (file)
@@ -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
index 2a22b18..cfefcfd 100644 (file)
@@ -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);
 
index d62f51d..92f7485 100644 (file)
@@ -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
index 8dc7a55..7daa8eb 100644 (file)
@@ -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);
 
index 80e2826..141dad5 100644 (file)
@@ -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);
 
index a1e514f..2abf80b 100644 (file)
@@ -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);
 
index 4ddd737..0e1e265 100644 (file)
@@ -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);
index 3ca6795..827f84a 100644 (file)
@@ -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");
index fb55a3c..4a4d318 100644 (file)
@@ -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) {
index 6aae6f1..0fd2153 100644 (file)
@@ -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 (file)
index 0000000..aef0cea
--- /dev/null
@@ -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
index 65c2af3..b178336 100644 (file)
@@ -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;
        }
 
        /*
index 9b730f1..575f5f5 100644 (file)
@@ -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;
 
index 0c11fce..29ab432 100644 (file)
@@ -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,
index 06e8467..91bd88f 100644 (file)
@@ -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 */
index 23e430d..18d7e72 100644 (file)
 #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
 
 #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
 #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
index 086eeea..5994e10 100644 (file)
@@ -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;
index 824d36f..93512dc 100644 (file)
@@ -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)
 {
index 2b876d4..3d5cd1b 100644 (file)
 #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;
index e23beae..0fd5567 100644 (file)
@@ -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);
index ea7b8b8..df318f5 100644 (file)
@@ -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);
index d66412a..3252dbe 100644 (file)
@@ -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},
index 730f325..c7c2c70 100644 (file)
@@ -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 */
 #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
 
 #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.
index 4452594..d82ab98 100644 (file)
@@ -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)
@@ -432,164 +452,6 @@ static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type)
        return SOF_COMP_NONE;
 }
 
-/*
- * 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;
        }
index 4c3cff0..b0e4556 100644 (file)
@@ -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);
index d38ebbb..da4b8f5 100644 (file)
@@ -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)
index 3c9a9de..81c407d 100644 (file)
@@ -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[] = {
index ef42733..e202675 100644 (file)
@@ -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;
        }
 
index cd4b235..3fd28ee 100644 (file)
@@ -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);
 
index ee448d5..34f3e0b 100644 (file)
@@ -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)
index e6d548f..dbed3c5 100644 (file)
@@ -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,
index 7aa3c32..8e53718 100644 (file)
@@ -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,
index 6604455..33c78d3 100644 (file)
@@ -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)
index e8446cc..700d936 100644 (file)
@@ -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 = {
index a90e0d7..394d8b2 100644 (file)
@@ -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)
index 48970ef..296c4ca 100644 (file)
@@ -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);
index efd374f..e08f4fe 100644 (file)
@@ -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 = {